/************************************************************************************************
 *   深圳市摩西尔电子有限公司 @版本所有@
 *
 *   此文件创建表格
 *
 * 修改:
 *   1. 类型 : 创建
 *      作者 : 巫昭雯
 *      时间 : 2019.12.11
 *      内容 : 所有代码
 *************************************************************************************************/

/*exported mc_svg_tb */
/*exported mc_svg_gp_select_cur */
/*exported set_trace_point */
/*exported merge_call_operation */
/*exported mc_svg_on_val_chg_callback */
/*exported test_fun */
/*exported get_virtual_point_data */
/*exported mc_util_tb_data */
/*exported get_g_max_val */
/*exported construct_color_pos_map */
/*exported get_next_dot_data */
/*exported get_merge_param */


/* global $ */
/* global mc_data_compute */
/* global show_info_massage */
/* global arr_svg_obj */
/* global arr_action_stack */
/* global ui_zha_ie_version */
/* global g_obj_lang */
/* global mc_util_is_array */
/* global mc_util_is_object */
/* global mc_util_copy_obj */
// /* global prompt_page_zoom */


var g_arr_svg_gp = [];


/************************************************************************************************
 * 类型:
 *    函数
 * 功能:
 *    创建表格函数
 * 参数:
 *    @param { Promise<Number> } row 创建表格的行数
 *    @param { Promise<Number> } col 创建表格的列数
 *    @param { Promise<Number> } id_name 创建表格的容器标签id名
 * 返回：
 *    @returns { Promise<Boolean> } true 输入参数类型正确 | false 输入参数类型错误;
 * 例子：
 *    NA
 * 备注：
 *    该表格具备功能：生成点，编辑点，复制点，渲染点，回撤点，设置点数据(行扫数,虚点值)
 * 修改:
 *   1. 类型 : 创建
 *      作者 : 巫昭雯
 *      时间 : 2019.12.17
 *      内容 : 所有代码
 ************************************************************************************************/
function mc_svg_tb(row, col, id_name) {
    if ("number" !== typeof row || "number" !== typeof col || "" === id_name.trim()) {
        return false;
    }

    var mc_tb_wrap_div = document.getElementById(id_name);

    if (!mc_tb_wrap_div) {
        return false;
    }

    // save this obj
    var m_this = this;

    // 设置外盒子定位,添加滚动条
    // mc_tb_wrap_div.style.position = "relative";
    // mc_tb_wrap_div.style.overflow = "scroll";

    // 格子总数
    var ui_grid_num = col * row;

    // 每个小格子宽高；固定
    var each_rect_width = 30;
    var each_rect_height = 20;

    // 测试数据
    each_rect_width = 60;
    each_rect_height = 25;

    // 测试数据
    // each_rect_width = 100;
    // each_rect_height = 40;

    // 保存边界值：选中时不能超过框框
    var rect_x_width = each_rect_width * (col - 1);
    var rect_y_height = each_rect_height * (row - 1);

    // 设置svg宽高
    var svg_width = each_rect_width * col;
    var svg_height = each_rect_height * row;

    // 创建svg对象
    var obj_svg = new create_svg();
    var obj_svg_head = new create_nav_svg(false);
    var obj_svg_nav = new create_nav_svg();

    // 设置平铺id
    var pattern_id = id_name + "_mc_svg_pattern";
    var pattern_rect_id = id_name + "_tile_rect";

    obj_svg.set_pattern_id(pattern_id);
    obj_svg.set_pattern_rect_id(pattern_rect_id);
    obj_svg_head.set_row_col(1, col);
    obj_svg_nav.set_row_col(row, 1);


    // ***************** 定义id *****************
    // 该 mc_svg_tb 最外层 id
    var str_item_wrap_id = add_id_tag("item_wrap");
    // 编辑框和位置值块id
    var edit_box_id = id_name + "_edit_box_div";
    var pos_box_id = id_name + "_pos_box_div";

    // svg块id
    var mc_svg_id = id_name + "_mc_svg";
    var select_all_div_id = id_name + "_select_all_div";
    var svg_item_content_id = id_name + "_svg_item_content";
    var form_box_id = id_name + "_form_box";

    // ***************** ***************** ***************** 重新啊布局 ***************** ***************** *****************
    var mc_main_wrap_id = id_name + "_mc_main_wrap";
    var mc_head_wrap_id = id_name + "_mc_head_wrap";
    var mc_nav_wrap_id = id_name + "_mc_nav_wrap";

    // 定义html字符串
    var str_edit_box_html = "<div class='show_box_wrap flex_box_horizontal'><div id='" + pos_box_id + "' class='edit_box_div pos_box_div'></div><div id='" + edit_box_id + "' div_type='" + get_div_type_val(1) + "' class='edit_box_div' contenteditable='true' tabindex='0' is_edit='false'></div></div>";
    var str_select_all_html = "<div id='" + select_all_div_id + "' class='select_all_div'></div>";
    var str_svg_main_html = "<div id=" + mc_main_wrap_id + " class='mc_svg_main_wrap'><div id='" + mc_svg_id + "' ondragstart = 'return false;' class='dbclick_unchecked mc_svg'>" + obj_svg.get_html_svg() + "</div></div>";
    var str_svg_head_html = "<div id=" + mc_head_wrap_id + " class='mc_svg_head_wrap'><div class='mc_svg_head'>" + obj_svg_head.get_html_svg() + "</div></div>";
    var str_svg_nav_html = "<div id=" + mc_nav_wrap_id + " class='mc_svg_nav_wrap'><div class='mc_svg_nav'>" + obj_svg_nav.get_html_svg() + "</div></div>";
    // 滚动条封边
    var str_scroll_html = "<div class='scroll_block scroll_ver'></div><div class='scroll_block scroll_hor'></div>";

    // 表格组合
    var str_table_html = "<div id='" + form_box_id + "' class='form_box' contenteditable='false'>" + str_select_all_html + str_svg_head_html + str_svg_main_html + str_svg_nav_html + str_scroll_html + "</div>";
    var str_html = str_edit_box_html + str_table_html;


    // **************************************** 初始化创建表格 **************************************** //
    // 将所有输出html包裹在该item最外层
    mc_tb_wrap_div.innerHTML = "<div id='" + str_item_wrap_id + "' tabindex='0' style='height:100%' class='uncheck_focus_style'>" + str_html + "</div> ";

    // 保存id值; hover块 和框选块 id
    var hover_content_id = "";
    var hover_head_id = "";
    var hover_nav_id = "";
    var box_selection_wrap_id = "";
    var small_div_id = "";
    // 拖拽虚拟div id
    var str_drag_virtual_div_id = id_name + "_" + "drag_virtual_div";


    // ***************** 定义变量保存创建的对象 *****************
    // 框选div
    var obj_box_selection_wrap_div = {};
    var obj_small_div = {};
    // 鼠标移入时div：hover
    var obj_div_hover_content = {};
    var obj_div_hover_head = {};
    var obj_div_hover_nav = {};
    // 编辑框
    var obj_edit_box_div = document.getElementById(edit_box_id);
    // 显示位置div
    var obj_pos_div = document.getElementById(pos_box_id);
    // 保存框选的div集合对象
    var arr_box_select_obj = [];
    // 建立位置公式的映射对象
    var obj_pos_formula = {};
    // 保存生成的对象集合;获取最后一个设置虚点
    var arr_create_obj = [];
    // 复制时拖拽区域存在的生成对象集合
    var arr_exist_obj_copy = [];
    // 保存svg的jq对象
    var $obj_svg_jq = $("#" + mc_svg_id);


    // ***************** 定义框选判断的值 *****************
    // 框选的第一个对象
    var first_obj = "";
    // 框选时是增值还是减值
    var b_is_add = true;
    // 框选步进值为left 或者是top
    var str_offset_step_val = "offsetLeft";
    // 框选步进值为width或是height
    var str_width_step_val = each_rect_width;
    // 判断生成块是否是触发点击之后触发移入事件
    var b_is_enter_after_down = false;
    // 判断选中的是横向还是纵向： 0:表示横向; 1:表示纵向; 2表示单个块选中;
    var ui_selected_xy = 2;
    // svg点击次数;计数
    var ui_click_num = 0;   // eslint-disable-line
    // 定义选中的上一个生成对象
    var obj_pre_create = {};
    // 页面当前是否为可选状态
    var b_sel_status = false;

    // save max formula value
    var m_ui_max_fm_val = 0;
    var m_str_color = "";
    // 移出模块定时器
    var m_ui_timer = null;

    // ***************** 保存外部数据 *****************
    // 扫数
    var ui_sweep_num = 1;
    // 保存累加的虚点值
    var ui_virtual_point = 0;
    // 虚点走点模式
    var b_is_virtual_point_mode = false;
    var ui_click_num_virtual = 0;


    // ***** 模组数据 *****
    // 单扫长度，行扫数，数据组数, 一组数据个数
    var obj_external_mod_data = {};
    // 添加在样式表添加类
    var arr_grp_color = new Array(obj_external_mod_data.ui_grp_num);


    // ***************** 定义常量 *****************
    // 定义符号
    var cui_equal_sign = "=";
    var cui_underline = "_";
    // 无效值 "Invalid"
    var cui_invalid_value = g_obj_lang.get_item_val("MC_LANG_INVALID");
    var cui_endless_loop = g_obj_lang.get_item_val("MC_LANG_ENDLESS_LOOP");
    // 判断整数正则
    var cui_reg_int = /[\d.]+/g;
    // 运算符号
    var cui_calc_symbol = ["+", "-", "*", "/", "="];
    // var cui_pos_symbol = ["(",")",":"];


    // **************************************** 定义this 属性 **************************************** //
    this.b_is_cur_obj = false;
    this.b_is_mousedown = false;


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    添加事件监听固定格式对象函数；提供 add_eventListener_multiple_fun 函数使用
     * 参数:
     *    @param { Promise<String> } event_name 添加事件名
     *    @param { Promise<Function> } event_fun 事件调用函数
     *    @param { Promise<Boolean> } event_bubble 是否事冒泡事件；可选参数；默认 false
     * 返回：
     *    @returns { Promise<Boolean> } 添加成功=== true || 失败 === false
     * 例子：
     *    NA
     * 备注：
     *    NA
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2019.5.20
     *      内容 : 所有代码
     ************************************************************************************************/
    var obj_event_format = function (event_name, event_fun, event_bubble) {
        if ("string" !== typeof event_name || "function" !== typeof event_fun) {
            return false;
        }
        this.event_name = event_name;
        this.event_fun = event_fun;
        this.event_bubble = event_bubble || false;
        return true;
    };


    // **************************************** 初始化其他 **************************************** //
    // 初始化创建背景高亮
    var str_fill_pat_id = id_name + "_" + "bgc_hl_pat";

    init_bgc_highlight(str_fill_pat_id);

    // 创建鼠标事件div
    create_div_mouse_event();


    // 给生成的div添加事件监听
    var arr_point_event = [
        new obj_event_format("mouseenter", create_div_mouseenter),
        new obj_event_format("keydown", create_div_keydown),
        new obj_event_format("dblclick", create_div_dblclick)
    ];

    // ============================图层监听事件=========================
    // 给svg监听移动事件：移动时生成的hover_div不会闪；
    // 给svg外层div添加鼠标点击事件，这样可以捕获处理生成的div_click事件
    // 然后给点击生成的div_click 添加点击事件个移动事件冒泡给外层div处理捕获svg事件

    // 保存对象
    var mc_svg = document.getElementById(mc_svg_id);
    var svg_item_content = document.getElementById(svg_item_content_id);


    // 图层添加监听事件
    var arr_svg_item_content_event = [
        new obj_event_format("mousemove", svg_mousemove),
        new obj_event_format("mouseout", svg_mouseout)
    ];

    add_eventListener_multiple_fun(svg_item_content, arr_svg_item_content_event);


    // 编辑框添加键盘事件监听
    var arr_edit_box_event = [
        new obj_event_format("blur", edit_box_div_blur, true),
        new obj_event_format("focus", edit_box_div_blur, true),
        new obj_event_format("keydown", edit_box_div_keydown),
        new obj_event_format("keyup", edit_box_div_keyup)
    ];

    add_eventListener_multiple_fun(obj_edit_box_div, arr_edit_box_event);

    // 表格整体svg；控制滚动条；
    var obj_form_box = document.getElementById(form_box_id);
    // 控制滚动条的对象
    var obj_scroll_wrap = document.getElementById(mc_main_wrap_id);

    scroll_event_listener(true);

    obj_form_box.addEventListener("mouseenter", function () {
        scroll_event_listener(true);
    }, false);
    obj_form_box.addEventListener("mouseleave", function () {
        scroll_event_listener(false);
        // 鼠标移出之后设置编辑框为不可编辑状态
        obj_edit_box_div.setAttribute("is_edit", false);
    }, false);


    // 测试数据；测试复制拖拽虚拟框
    // $obj_svg_jq.append("<div class='test_div'></div>");

    g_arr_svg_gp.push(this);

    // 块数据变动集合;二维数组堆栈
    var arr_block_data_change = [];
    var arr_block_data_change_sub = [];

    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    构造块数据;提供给arr_block_data_change使用
     * 参数:
     *    @param { Promise<Object> } param 对象参数
     * 返回：
     *    NA
     * 例子：
     *    NA
     * 备注：
     *    b_is_init:表示当前数据是否是初始数据
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2019.4.17
     *      内容 : 所有代码
     ************************************************************************************************/
    var obj_block_data = function (param) {
        this.b_is_init = param.b_is_init;
        this.pos = param.pos;
        this.init_fml = param.init_fml;
        this.formula = param.formula;
        this.point = param.point;
        this.sweep = param.sweep;
        this.div_id = param.id;
        this.cur_arr_box_select = arr_box_select_obj;
    };


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    设置模组数据
     * 参数:
     *    @param { Promise<Object> } obj 对象参数
     * 返回：
     *    NA
     * 例子：
     *    var obj_mod_data = {
     *      ui_single_sweep_len: g_ui_mod_scan_len,
     *      ui_line_scan_num: g_ui_mod_scan_cnt,
     *      ui_grp_num: g_ui_mod_str_cnt,
     *      ui_single_grp_num: g_ui_mod_scan_len * g_ui_mod_scan_cnt
     *     }
     * 备注：
     *    obj 包含数据为 单扫长度，行扫数，数据组数, 一组数据个数
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2019.6.4
     *      内容 : 所有代码
     ************************************************************************************************/
    this.set_mod_data = function (obj) {
        if ("object" !== typeof obj || "{}" === JSON.stringify(obj)) {
            return;
        }

        obj_external_mod_data = obj;
        add_grp_css_rules();
    };

    //  接收设置的硬件测试回调函数
    var hardware_callback_fun = null;

    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    设置走点数据变动硬件测试回调函数
     * 参数:
     *    @param { Promise<Function>} callback 硬件测试回调函数
     * 返回：
     *    NA
     * 例子：
     *    NA
     * 备注：
     *    hardware_callback_fun 调用时返回执行码；以及调用传参
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2020.5.21
     *      内容 : 所有代码
     ************************************************************************************************/
    this.set_data_chg_hardware_callback = function (callback) {
        if ("function" !== typeof callback) {
            return;
        }
        hardware_callback_fun = callback;
    };


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    调用硬件测试回调函数
     * 参数:
     *    @param { Promise<Number>}  code 回执行码
     *    @param { Promise<All>}  params 调用传参
     * 返回：
     *    NA
     * 例子：
     *    NA
     * 备注：
     *    NA
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2020.5.21
     *      内容 : 所有代码
     ************************************************************************************************/
    function mc_svg_tb_hardware_callback(code, params) {
        if ("number" !== typeof code) {
            return;
        }

        if (null !== hardware_callback_fun) {
            hardware_callback_fun(code, params);
        }
    }

    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    添加id关联外部标记
     * 参数:
     *    @param { Promise<String>} str 设置的id名
     * 返回：
     *    @returns { Promise<String>} 成功返回带标记的id || 返回输入参数
     * 例子：
     *    NA
     * 备注：
     *    该外部标记为调用 mc_svg_tb 对象函数的id传参
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2020.5.21
     *      内容 : 所有代码
     ************************************************************************************************/
    function add_id_tag(str) {
        if ("string" !== typeof str) {
            return str;
        }

        return id_name + "_" + str;
    }


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    添加或移除滚动条监听
     * 参数:
     *    @param { Promise<Boolean> } b_add 添加 === true  || 移除 === false
     * 返回：
     *    NA
     * 例子：
     *    NA
     * 备注：
     *    NA
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2019.4.15
     *      内容 : 所有代码
     ************************************************************************************************/
    function scroll_event_listener(b_add) {
        if ("boolean" !== typeof b_add) {
            return;
        }
        if (b_add) {
            // obj_scroll_wrap.addEventListener("scroll", scroll_event);
            obj_scroll_wrap.onscroll = scroll_event;
        } else {
            // obj_scroll_wrap.removeEventListener("scroll", scroll_event);
            // obj_scroll_wrap.onscroll = null;
        }
    }


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    设置（移除 || 添加）滚动条事件监听；
     * 参数:
     *    @param { Promise<Boolean> } b_add 添加 === true  || 移除 === false
     * 返回：
     *    NA
     * 例子：
     *    NA
     * 备注：
     *    为外部函数使用
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2019.4.15
     *      内容 : 所有代码
     ************************************************************************************************/
    this.set_scroll_event_listener = function (b_add) {
        if ("boolean" !== typeof b_add) {
            return;
        }

        scroll_event_listener(b_add);
    };


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    生成块鼠标移入事件
     * 参数:
     *    @param { Promise<Object> } event 鼠标移动事件默认参数
     * 返回：
     *    NA
     * 例子：
     *    NA
     * 备注：
     *    鼠标移动需要判断是单纯移动还是鼠标拖拽；
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2019.12.17
     *      内容 : 所有代码
     ************************************************************************************************/
    function create_div_mouseenter(event) {
        // console.log("create_div_mouseenter");

        // 当存在虚拟div时禁止掉生成块的鼠标事件
        if (document.getElementById(str_drag_virtual_div_id)) {
            $("#" + mc_svg_id + " .click_create_div").css("pointer-events", "none");
            return;
        }

        $("#" + mc_svg_id + " .click_create_div").css("pointer-events", "auto");

        if (0 !== event.button) {
            return;
        }

        // 鼠标移动时hover块移动
        set_hover_div_style_mousemove(true, event);

        // 定义选中的div是否为编辑状态
        // var b_is_edit_status = false;
        this.b_edit_status = false;

        //  定义鼠标按下释放事件
        this.onmousedown = create_div_mouseenter_down;
        this.onmouseup = create_div_mouseenter_up;
        // this.ondblclick = create_div_dblclick;

        // 选中移动时变色
        if (b_is_enter_after_down) {
            // var obj_cur_div = document.getElementById(event.currentTarget.attributes.id.value);
            var obj_cur_div = event.currentTarget;

            // 框选对象
            var arr_selected_div = arr_box_select_obj;
            var arr_selected_div_len = arr_selected_div.length;

            // 鼠标移动前后对象；兼容浏览器
            var from_obj = event.fromElement || event.relatedTarget;
            var to_obj = event.toElement || event.target;

            if (from_obj === first_obj) {
                juge_box_select(event);
            }

            // 横轴 || 纵轴
            var selecte_x = 0 === ui_selected_xy && to_obj.offsetTop === first_obj.offsetTop && from_obj.offsetTop === first_obj.offsetTop;
            var selecte_y = 1 === ui_selected_xy && from_obj.offsetLeft === first_obj.offsetLeft && to_obj.offsetLeft === first_obj.offsetLeft;

            // 合并
            if (selecte_x || selecte_y) {
                var from_top_val = first_obj[str_offset_step_val] - (arr_selected_div_len - 1) * str_width_step_val;
                var to_top_val = first_obj[str_offset_step_val] - arr_selected_div_len * str_width_step_val;

                if (b_is_add) {
                    from_top_val = first_obj[str_offset_step_val] + (arr_selected_div_len - 1) * str_width_step_val;
                    to_top_val = first_obj[str_offset_step_val] + arr_selected_div_len * str_width_step_val;
                }

                if (from_obj[str_offset_step_val] === from_top_val && to_obj[str_offset_step_val] === to_top_val) {
                    obj_cur_div.classList.add("box_selection_creat_div");
                    if (-1 === arr_box_select_obj.indexOf(event.target)) {
                        arr_box_select_obj.push(event.target);
                    }
                }
            }
        } else {
            // 判断当前是否已经时框选了
            if (0 !== ui_selected_xy && 1 !== ui_selected_xy) {
                ui_selected_xy = 2;
            }
        }
    }


    /************************************************************************************************
    * 类型:
    *    函数
    * 功能:
    *    生成块鼠标按下事件; 鼠标移入时-->触发鼠标按下事件
    * 参数:
    *    @param { Promise<Object> } event_down 鼠标按下事件默认参数
    * 返回：
    *    NA
    * 例子：
    *    NA
    * 备注：
    *    鼠标按下事件为按下左键时才触发
    * 修改:
    *   1. 类型 : 创建
    *      作者 : 巫昭雯
    *      时间 : 2019.12.17
    *      内容 : 所有代码
    ************************************************************************************************/
    function create_div_mouseenter_down(event_down) {
        if (0 === event_down.button) {
            event_down.stopPropagation();
            var obj_cur = event_down.currentTarget;
            var b_contenteditable = obj_cur.isContentEditable;
            // console.log("生成块鼠标按下");

            // 当前div为可编辑状态
            if (b_contenteditable) {
                return;
            }

            if (is_edit_status()) {
                // b_is_edit_status = true;
                this.b_edit_status = true;
                b_sel_status = true;

                // 编辑状态下不可选择自己
                if (-1 !== arr_box_select_obj.indexOf(obj_cur)) {
                    return;
                }

                // 阻止触发编辑框的失焦事件;当生成块为可输入状态时;阻止默认点击输入;
                event_down.preventDefault();
                var x_val = obj_cur.getAttribute("x");
                var y_val = obj_cur.getAttribute("y");
                var left = obj_cur.offsetLeft;
                var top = obj_cur.offsetTop;
                var id = "edit_" + x_val + "_" + y_val;

                create_pos_div(id, left, top);
                add_cursor_pos_val(x_val, y_val);
                return;
            }

            // 拖拽
            b_is_enter_after_down = true;
            // 框选的第一个对象
            first_obj = obj_cur;

            // 保存框选对象
            arr_box_select_obj = [event_down.target];
            // arr_box_select_obj.push();

            // 框选之后不做任何操作又点击svg时隐藏框选div外框
            obj_box_selection_wrap_div.attributes.style.value = "display:none";
            change_class_after_selector(obj_cur);
        }

        if (2 === event_down.button) {
            // 鼠标点击右键
            event_down.stopPropagation();
            uncheck_create_block();
        }
    }


    /************************************************************************************************
    * 类型:
    *    函数
    * 功能:
    *    生成块鼠标释放事件; 鼠标移入时-->释放鼠标
    * 参数:
    *    @param { Promise<Object> } event_up 鼠标释放事件默认参数
    * 返回：
    *    NA
    * 例子：
    *    NA
    * 备注：
    *    鼠标释放事件为按下左键时才触发
    * 修改:
    *   1. 类型 : 创建
    *      作者 : 巫昭雯
    *      时间 : 2019.12.17
    *      内容 : 所有代码
    ************************************************************************************************/
    function create_div_mouseenter_up(event_up) {
        // console.log("create_div_mouseenter_up", event_up);
        if (0 === event_up.button) {
            if (!m_this.b_is_mousedown) {
                // m_this.b_is_mousedown = false;
                // return;
            }
            event_up.stopPropagation();
            // event.preventDefault();
            var obj_cur = event_up.currentTarget || event_up.target;
            var b_contenteditable = obj_cur.isContentEditable;

            // 当前div为可编辑状态
            if (b_contenteditable) {
                return;
            }

            // 编辑选中
            if (this.b_edit_status) {
                return;
            }

            // 设置上一个生成对象不可编辑
            if (obj_pre_create) {
                var obj_pre_edit = obj_pre_create.isContentEditable;

                // console.log("上一个选中对象",obj_pre_edit);

                if (true === obj_pre_edit) {
                    obj_pre_create.contentEditable = false;
                }
            }


            b_is_enter_after_down = false;
            set_style_after_drag(event_up, ui_selected_xy);
            set_edit_box_val(obj_cur.getAttribute("formula"));

            return;
        }
    }


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    取消选中；(图层鼠标右击事件：)
     * 参数:
     *    NA
     * 返回：
     *    NA
     * 例子：
     *    NA
     * 备注：
     *    NA
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2019.12.17
     *      内容 : 所有代码
     ************************************************************************************************/
    function uncheck_create_block() {
        var arr_create_div = arr_create_obj;
        var arr_create_div_len = arr_create_div.length;

        // 移除选中类并设置pointer-events为auto
        if (0 !== arr_create_div_len) {
            for (var idx = 0; idx < arr_create_div_len; idx++) {
                var item = arr_create_div[idx];
                var class_list = item.classList;

                item.style.pointEvvents = "auto";
                class_list.remove("box_selection_creat_div");
            }
        }
        var obj_virtual_div = document.getElementById(str_drag_virtual_div_id);

        if (obj_virtual_div) {
            obj_virtual_div.parentElement.removeChild(obj_virtual_div);
        }

        // 隐藏框选外框
        obj_box_selection_wrap_div.attributes.style.value = "display:none";
        // 移除框选对象
        arr_box_select_obj = [];
        arr_box_select_obj.length = 0;
    }


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    判断框选数据
     * 参数:
     *    @param { Promise<Object> } event 鼠标事件event
     * 返回：
     *    NA
     * 例子：
     *    NA
     * 备注：
     *    NA
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2020.4.3
     *      内容 : 所有代码
     ************************************************************************************************/
    function juge_box_select(event) {
        // 兼容浏览器
        var to_obj = event.toElement || event.target;

        if (first_obj.offsetTop === to_obj.offsetTop) {
            ui_selected_xy = 0;
            b_is_add = true;
            str_offset_step_val = "offsetLeft";
            str_width_step_val = each_rect_width;
            if (first_obj.offsetLeft > to_obj.offsetLeft) {
                b_is_add = false;
            }
        } else if (first_obj.offsetLeft === to_obj.offsetLeft) {
            ui_selected_xy = 1;
            b_is_add = true;
            str_offset_step_val = "offsetTop";
            str_width_step_val = each_rect_height;
            if (first_obj.offsetTop > to_obj.offsetTop) {
                b_is_add = false;
            }
        }
    }


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    移除所有样式选中当前点击块样式;
     * 参数:
     *    @param { Promise<Object> } obj_cur 当前块对象;
     * 返回：
     *    NA
     * 例子：
     *    NA
     * 备注：
     *    NA
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2020.4.3
     *      内容 : 所有代码
     ************************************************************************************************/
    function change_class_after_selector(obj_cur) {
        if ("undefined" !== typeof obj_cur && obj_cur) {
            remove_selected_div_class();
            obj_cur.classList.add("box_selection_creat_div");
        }
    }


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    拖拽结束之后设置框选div样式:  box_selection_wrap_div
     * 参数:
     *    @param { Promise<Object> } event 事件对象
     *    @param { Promise<String> } dir_xy 框选的是x轴或y轴方向
     * 返回：
     *    NA
     * 例子：
     *    NA
     * 备注：
     *    x轴或y轴方向参数解释：
     *      0:表示横向;
     *      1:表示纵向;
     *      其他：表示是单个选中
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2019.12.17
     *      内容 : 所有代码
     ************************************************************************************************/
    function set_style_after_drag(event, dir_xy) {
        var arr_boxsele_div = arr_box_select_obj;
        var arr_len = arr_boxsele_div.length;

        // 判断如果是再外层拖拽的话就return
        if ("string" !== typeof event) {
            if (svg_item_content_id === event.currentTarget.id && 0 === arr_len) {
                return;
            }
        }

        if (0 === arr_len) {
            obj_box_selection_wrap_div.style.cssText += "top:0px;left:0px;width:0px;height:0px;border:0";
            return;
        }

        // set_box_selecte_style;

        var boxsele_width = each_rect_width;
        var boxsele_height = each_rect_height;
        var boxsele_top = arr_boxsele_div[0].offsetTop;
        var boxsele_left = arr_boxsele_div[0].offsetLeft;

        // 设置位置值
        if (1 === arr_len) {
            var x_val = Number(boxsele_left) / each_rect_width + 1;
            var y_val = Number(boxsele_top) / each_rect_height + 1;
            var pox_val = "(" + x_val + ":" + y_val + ")";

            set_pos_div_val(pox_val);

            // 保存单个框选对象框选对象
            obj_pre_create = arr_boxsele_div[0];
        }


        // 定义小点点的大小(设置的小块的宽高)
        var small_div_size = 8;

        // 框选之后边框宽高变化：before :表示右边框(右边框宽固定)；after:表示下边框(下边框高固定)
        var border_width_before = 2;
        var border_height_before = each_rect_height - small_div_size;
        var border_width_after = each_rect_width - small_div_size;
        var border_height_after = 2;
        var str_styles_idx = get_cur_stylesheets_idx();


        // 0:表示横向; 1:表示纵向; 其他：表示是单个选中
        if (0 === dir_xy) {
            boxsele_width = each_rect_width * arr_len;
            boxsele_height = each_rect_height;
            boxsele_top = arr_boxsele_div[0].offsetTop;
            boxsele_left = get_boxsele_div_min_pos(arr_boxsele_div, "offsetLeft");
            border_height_before = each_rect_height - small_div_size;
            border_width_after = boxsele_width - small_div_size;
        } else if (1 === dir_xy) {
            boxsele_width = each_rect_width;
            boxsele_height = each_rect_height * arr_len;
            boxsele_top = get_boxsele_div_min_pos(arr_boxsele_div, "offsetTop");
            boxsele_left = arr_boxsele_div[0].offsetLeft;
            border_height_before = boxsele_height - small_div_size;
            border_width_after = each_rect_width - small_div_size;
        }

        var str_style_rule_before = "content:'';position: absolute;right: 0;height:" + border_height_before + "px;width:" + border_width_before + "px;background-color:#025619;";
        var str_style_rule_after = "content:'';position: absolute;bottom:0;height:" + border_height_after + "px;width:" + border_width_after + "px;background-color:#025619;";

        if ("undefined" !== str_styles_idx && false !== str_styles_idx) {
            document.styleSheets[str_styles_idx].addRule("#" + box_selection_wrap_id + ":before", str_style_rule_before);
            document.styleSheets[str_styles_idx].addRule("#" + box_selection_wrap_id + ":after", str_style_rule_after);

            obj_box_selection_wrap_div.style.cssText = "width:" + boxsele_width + "px;height:" + boxsele_height + "px;top:" + boxsele_top + "px;left:" + boxsele_left + "px;display:block;";
            // obj_box_selection_wrap_div.style.transition = "all 0.3s";
            // $(obj_box_selection_wrap_div).css({
            //     width: boxsele_width + "px",
            //     height: boxsele_height + "px",
            //     top: boxsele_top,
            //     left: boxsele_left,
            //     display: "block"
            //     transition:'all 0.5s'
            // });
        }
    }

    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    设置框选边框的样式
     * 参数:
     *    NA
     * 返回：
     *    @returns { Promise<Number> } 返回指定样式表在样式表数组中的下标
     *    @returns { Promise<Boolean> } false:找到不到指定的样式表
     * 例子：
     *    NA
     * 备注：
     *    NA
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2019.12.17
     *      内容 : 所有代码
     ************************************************************************************************/
    function get_cur_stylesheets_idx() {
        var obj_styleSheets = document.styleSheets;

        for (var idx = 0; idx < obj_styleSheets.length; idx++) {
            var str_styles_id = obj_styleSheets[idx].ownerNode.id;

            if ("undefined" !== str_styles_id && "trace_point_css" === str_styles_id) {
                return idx;
            }
        }

        return false;
    }


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    获取框选div最小位置值: top | left值
     * 参数:
     *    @param { Promise<Array> } arr 框选的块数组
     *    @param { Promise<String> } val_name 属性名
     * 返回：
     *    @returns { Promise<String> } 返回最小位置值
     * 例子：
     *    NA
     * 备注：
     *    NA
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2019.12.17
     *      内容 : 所有代码
     ************************************************************************************************/
    function get_boxsele_div_min_pos(arr, val_name) {
        var min_xy_pos = arr[0][val_name];

        for (var idx = 0; idx < arr.length; idx++) {
            if (arr[idx][val_name] < min_xy_pos) {
                min_xy_pos = arr[idx][val_name];
            }
        }
        return min_xy_pos;
    }

    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    框选之后small_div点击事件
     * 参数:
     *    @param { Promise<Object> } event 事件对象参数
     * 返回：
     *    NA
     * 例子：
     *    NA
     * 备注：
     *    NA
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2019.12.17
     *      内容 : 所有代码
     ************************************************************************************************/
    function small_div_mousedown(event) {
        event.stopPropagation();
        if (0 === event.button) {
            // console.log("small_div_mousedown");

            var str_virtual_div = "<div id='" + str_drag_virtual_div_id + "' class='drag_virtual_div' style=''></div>";

            $obj_svg_jq.append(str_virtual_div);

            $("#" + mc_svg_id + " .click_create_div").css("pointer-events", "none");
            $("#" + str_drag_virtual_div_id).css({
                "width": obj_box_selection_wrap_div.offsetWidth,
                "height": obj_box_selection_wrap_div.offsetHeight,
                "top": obj_box_selection_wrap_div.offsetTop,
                "left": obj_box_selection_wrap_div.offsetLeft
            });

            this.onmousemove = function () {
                // var left_pos = each_rect_width * Math.floor(event_move.offsetX / each_rect_width);
                // var top_pos = each_rect_height * Math.floor(event_move.offsetY / each_rect_height);
            };

            this.onmouseup = function (event_up) {
                if (0 === event_up.button) {
                    var virtual_obj = document.getElementById(str_drag_virtual_div_id);

                    virtual_obj.parentElement.removeChild(virtual_obj);

                    // m_this.b_is_mousedown = false;
                }
            };
            return;
        } else if (2 === event.button) {
            event.stopPropagation();
            obj_box_selection_wrap_div.attributes.style.value = "display:none";
            remove_selected_div_class();
        }
    }

    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    框选之后框选外框点击事件
     * 参数:
     *    NA
     * 返回：
     *    NA
     * 例子：
     *    NA
     * 备注：
     *    NA
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2019.12.17
     *      内容 : 所有代码
     ************************************************************************************************/
    // function boxsele_wrap_div_mousedown() {}


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    判断是否为编辑状态
     * 参数:
     *    NA
     * 返回：
     *   @returns { Promise<Boolean> } true 当前为编辑状态 | false 当前不为编辑状态
     * 例子：
     *    NA
     * 备注：
     *    NA
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2020.4.1
     *      内容 : 所有代码
     ************************************************************************************************/
    function is_edit_status() {
        var edit_val = get_obj_innerText_val(obj_edit_box_div);
        var edit_val_len = edit_val.length;
        var b_is_edit = obj_edit_box_div.getAttribute("is_edit");

        var obj_sel = arr_box_select_obj[0];

        if (obj_sel) {
            // 若当前框选对象的公式和编辑框的公式相同则编辑状态为false
            if (edit_val === obj_sel.getAttribute("formula")) {
                remove_edit_pos_div();
                return false;
            }
        }


        if (0 !== edit_val_len) {
            var last_num = edit_val[edit_val_len - 1];

            // 若输入值最后一个不为运算符号return false
            if (-1 === cui_calc_symbol.indexOf(last_num)) {
                // remove_edit_pos_div();
                // return false;
            }
        }


        if (-1 !== edit_val.indexOf("=") && "true" === b_is_edit) {
            // console.log("编辑状态true");
            return true;
        }

        // console.log("编辑状态false");
        return false;
    }


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    鼠标移动时设置hover_div样式
     * 参数:
     *   @param { Promise<Boolean> } b_is_exist_div true 当前存在生成块 | false 当前不存在生成块
     *   @param { Promise<Object> } event 鼠标释放事件默认参数
     * 返回：
     *    NA
     * 例子：
     *    NA
     * 备注：
     *    NA
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2019.12.17
     *      内容 : 所有代码
     ************************************************************************************************/
    function set_hover_div_style_mousemove(b_is_exist_div, event) {
        var left_pos = "";
        var top_pos = "";

        //  判断鼠标移动时的位置是否存在div
        if (b_is_exist_div) {
            left_pos = event.currentTarget.offsetLeft;
            top_pos = event.currentTarget.offsetTop;
        } else {
            var top_y = event.offsetY / each_rect_height;
            var left_x = event.offsetX / each_rect_width;

            // 边界值
            if (top_y !== row && left_x !== col) {
                left_pos = each_rect_width * Math.floor(left_x);
                top_pos = each_rect_height * Math.floor(top_y);
            }
        }

        // 设置鼠标移动时的div位置
        obj_div_hover_nav.style.cssText += "top:" + top_pos + "px;display:block";
        obj_div_hover_head.style.cssText += "left:" + left_pos + "px;display:block";
        obj_div_hover_content.style.cssText += "top:" + top_pos + "px;left:" + left_pos + "px;line-height:" + each_rect_height + "px;display:block";
    }


    // **************************************** 构造函数:svg块, 坐标尺, 高亮背景 **************************************** //
    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    创建主体svg平埔矩阵图层
     * 参数:
     *    NA
     * 返回：
     *    NA
     * 例子：
     *    NA
     * 备注：
     *    NA
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2019.12.17
     *      内容 : 所有代码
     ************************************************************************************************/
    function create_svg() {
        // var rect_fill = "#fff";

        var rect_stroke = "#666";
        var rect_stroke_dasharray = "" + each_rect_width + "," + (Number(each_rect_width) + Number(each_rect_height)) + "";

        // var pattern_rect_id = "";
        var pattern_rect_fill = "rgba(255, 255, 255, 0.8)";
        var pattern_rect_stroke = "#666";

        // var pattern_id = "";


        /************************************************************************************************
         * 类型:
         *    函数
         * 功能:
         *    设置pattern_id
         * 参数:
         *    @param { Promise<String> } str svg平铺pattern属性id名
         * 返回：
         *    @returns { Promise<Boolean> } true 执行成功
         * 例子：
         *    NA
         * 备注：
         *    NA
         * 修改:
         *   1. 类型 : 创建
         *      作者 : 巫昭雯
         *      时间 : 2019.12.17
         *      内容 : 所有代码
         ************************************************************************************************/
        this.set_pattern_id = function (str) {
            if ("string" !== typeof str) {
                return false;
            }

            pattern_id = str;

            return true;
        };


        /************************************************************************************************
         * 类型:
         *    函数
         * 功能:
         *    设置平铺内小格子rect的id
         * 参数:
         *    @param { Promise<String>} str 设置的svg平铺pattern属性内的rect的id名
         * 返回：
         *    @returns { Promise<Boolean>} true || false
         * 例子：
         *    NA
         * 备注：
         *    NA
         * 修改:
         *   1. 类型 : 创建
         *      作者 : 巫昭雯
         *      时间 : 2019.12.17
         *      内容 : 所有代码
         ************************************************************************************************/
        this.set_pattern_rect_id = function (str) {
            if ("string" !== typeof str) {
                return false;
            }
            pattern_rect_id = str;

            return true;
        };

        /************************************************************************************************
         * 类型:
         *    函数
         * 功能:
         *    获取生成主体svg的html字串
         * 参数:
         *    NA
         * 返回：
         *    @returns { Promise<String> } 生成主体svg的html字串
         * 例子：
         *    NA
         * 备注：
         *    NA
         * 修改:
         *   1. 类型 : 创建
         *      作者 : 巫昭雯
         *      时间 : 2019.12.17
         *      内容 : 所有代码
         ************************************************************************************************/
        this.get_html_svg = function () {
            // 步骤
            // 设置每个小格子宽高固定值
            // 设置svg宽高;然后平铺矩阵宽高设置100%
            // 设置pattern宽高

            var pattern_width = each_rect_width / svg_width;
            var pattern_height = each_rect_height / svg_height;

            return "<svg  id='" + svg_item_content_id + "' width='" + svg_width + "' height='" + svg_height + "' version='1.1' xmlns='http://www.w3.org/2000/svg'><defs><pattern id='" + pattern_id + "' x='0' y='0' width='" +
                pattern_width + "' height='" + pattern_height + "'><rect class='' stroke-linecap='round' id='" + pattern_rect_id + "' x='0' y='0' width='" + each_rect_width + "' height='" + each_rect_height + "' fill='" + pattern_rect_fill + "' stroke-dasharray='" + rect_stroke_dasharray + "' stroke-width='1' stroke='" + pattern_rect_stroke +
                "'></rect></pattern></defs><rect  id='svg_item_content_rect' x='0' y='0' width='100%' height='100%' fill='url(#" + pattern_id + ")' stroke='" + rect_stroke + "' div_type='" + get_div_type_val(2) + "'></rect></svg>";
        };
    }

    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    创建导航栏svg
     * 参数:
     *    @param { Promise<Boolean> } b_nav 是否为侧边导航;  false === 头部导航(横轴) || 其他(默认侧边导航) === 侧边导航(竖轴)
     * 返回：
     *    NA
     * 例子：
     *    NA
     * 备注：
     *    根据输入的行列数控制显示头部以及侧边导航栏;
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2019.12.17
     *      内容 : 所有代码
     ************************************************************************************************/
    function create_nav_svg(b_nav) {
        // 定义侧边导航
        this.b_nav = true;

        if (false === b_nav) {
            this.b_nav = false;
        }

        // 文本
        var text_x = "";
        var text_y = "";
        var text_content = "";

        // 设置导航rect
        var nav_rect_fill = "rgba(238,238,238, 0.8)";
        var nav_rect_stroke = "#666";

        // 定义边框方向
        var rect_border_dir = "";
        var text_class = "";

        // 设置循环次数
        var loop_num = null;

        // 导航栏宽高
        var svg_div_width = 30;
        var svg_div_height = 30;

        // 边界值
        var rect_max_x = "";
        var rect_max_y = "";

        /************************************************************************************************
         * 类型:
         *    函数
         * 功能:
         *    设置外层div的对象
         * 参数:
         *    @param { Promise<Object> } obj 外层div对象
         * 返回：
         *    NA
         * 例子：
         *    NA
         * 备注：
         *    根据外层对象设置当前图层的宽高
         * 修改:
         *   1. 类型 : 创建
         *      作者 : 巫昭雯
         *      时间 : 2019.12.17
         *      内容 : 所有代码
         ************************************************************************************************/
        this.set_wrap_div = function (obj) {
            if ("undefined" !== typeof obj) {
                svg_div_width = obj.offsetWidth;
                svg_div_height = obj.offsetHeight;
            }
        };


        /************************************************************************************************
         * 类型:
         *    函数
         * 功能:
         *    设置几行几列
         * 参数:
         *    @param { Promise<Number> } str_row 设置行数
         *    @param { Promise<Number> } str_col 设置列数
         * 返回：
         *    NA
         * 例子：
         *    NA
         * 备注：
         *    根据输入的行列数控制显示头部以及侧边导航栏
         * 修改:
         *   1. 类型 : 创建
         *      作者 : 巫昭雯
         *      时间 : 2019.12.17
         *      内容 : 所有代码
         ************************************************************************************************/
        this.set_row_col = function (str_row, str_col) {
            if ("undefined" !== typeof row && "undefined" !== typeof col) {
                if (this.b_nav) {
                    loop_num = str_row;
                } else {
                    loop_num = str_col;
                }
            }
        };

        /************************************************************************************************
         * 类型:
         *    函数
         * 功能:
         *    获取边界值 0 === x轴;other === y轴
         * 参数:
         *    @param { Promise<Object> } str 外层div对象
         * 返回：
         *    @returns { Promise<Boolean> } true 设置成功
         * 例子：
         *    NA
         * 备注：
         *    根据输入的行列数控制显示头部以及侧边导航栏
         * 修改:
         *   1. 类型 : 创建
         *      作者 : 巫昭雯
         *      时间 : 2019.12.17
         *      内容 : 所有代码
         ************************************************************************************************/
        this.get_b_val = function (str) {
            if ("undefined" !== typeof str) {
                if ("x" === str) {
                    return rect_max_x;
                }
                return rect_max_y;
            }
            return false;
        };

        /************************************************************************************************
         * 类型:
         *    函数
         * 功能:
         *    获取生成svg的html字串
         * 参数:
         *    NA
         * 返回：
         *    @returns { Promise<String> } 生成svg的html字串
         * 例子：
         *    NA
         * 备注：
         *    NA
         * 修改:
         *   1. 类型 : 创建
         *      作者 : 巫昭雯
         *      时间 : 2019.12.17
         *      内容 : 所有代码
         ************************************************************************************************/
        this.get_html_svg = function () {
            var line_text_html = "";

            // 设置矩阵宽高,设置矩阵x,y轴
            var rect_width = each_rect_width;
            var rect_height = each_rect_height;
            var rect_x = "";
            var rect_y = "";
            var rect_stroke_width = 0.5;

            // 导航div里面的svg宽高；有边框差值所以+1
            var nav_sgv_width = svg_width + 1;
            var nav_svg_height = svg_height + 1;


            for (var idx = 0; idx < loop_num; idx++) {
                // 有边框差值
                rect_x = idx * rect_width + 0.5;
                rect_y = idx * rect_height + 0.3;

                text_x = rect_x + (rect_width / 2);
                // text_y = rect_y + (rect_width / 2);
                text_y = rect_y + (rect_height / 2);


                if (this.b_nav) {
                    // 侧边
                    nav_sgv_width = "100%";
                    rect_x = 0;
                    rect_width = svg_div_width;
                    text_x = rect_width / 2;
                    rect_border_dir = "rect_border_top";
                    text_class = "text_nav";
                } else {
                    // 头部
                    nav_svg_height = "100%";
                    rect_y = 0;
                    rect_height = svg_div_height;
                    text_y = rect_height / 2;
                    rect_border_dir = "rect_border_left";
                    text_class = "text_head";
                }

                text_content = Number(idx) + 1;
                line_text_html += "<rect class='" + rect_border_dir + "' x='" + rect_x + "' y='" + rect_y + "' width='" + rect_width + "' height='" + rect_height + "' fill='" +
                    nav_rect_fill + "' stroke-width='" + rect_stroke_width + "' stroke='" + nav_rect_stroke +
                    "'></rect><text class='" + text_class + "' x='" + text_x + "' y='" + text_y + "' alignment-baseline='middle' text-anchor='middle'>" + text_content + "</text>";
            }

            return "<svg  width='" + nav_sgv_width + "' height='" + nav_svg_height + "' version='1.1' xmlns='http://www.w3.org/2000/svg'>" + line_text_html + "</svg>";
        };
    }

    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    构造背景高亮格子
     * 参数:
     *    @param { Promise<Object> } obj_wrap 外部包裹的元素dom
     * 返回:
     *    @returns { Promise<Boolean> } false === 参数错误
     * 例子:
     *    NA
     * 备注:
     *    NA
     * 修改:
     *    1. 类型 : 创建
     *       作者 : 巫昭雯
     *       时间 : 2020-12-12
     *       内容 : 所有代码
    ************************************************************************************************/
    function create_bgc_highlight_rect(obj_wrap) {
        if (!obj_wrap) {
            return false;
        }

        var str_bgc_pat_id = "";
        var pattern_width = each_rect_width / svg_width;
        var pattern_height = each_rect_height / svg_height;
        var ui_loop_num = 0;
        var str_dir = "";
        var str_rect_width = each_rect_width;
        var str_rect_height = each_rect_height;
        var ui_step = 0;
        var str_rect_class = "";
        var obj_wrap_svg = obj_wrap;


        // 初始化创建defs; 返回创建的def节点
        function init_defs() {
            // defs
            var obj_bgc_defs = document.createElementNS("http://www.w3.org/2000/svg", "defs");
            // defs inner pattern
            var obj_bgc_pattern = document.createElementNS("http://www.w3.org/2000/svg", "pattern");

            obj_bgc_pattern.setAttribute("id", str_bgc_pat_id);
            obj_bgc_pattern.setAttribute("x", 0);
            obj_bgc_pattern.setAttribute("y", 0);
            obj_bgc_pattern.setAttribute("width", pattern_width);
            obj_bgc_pattern.setAttribute("height", pattern_height);

            // defs pattern inner rect
            var obj_bgc_fill_rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");

            obj_bgc_fill_rect.setAttribute("id", str_bgc_pat_id + "_" + "pattern_in_rect");
            obj_bgc_fill_rect.setAttribute("x", 0);
            obj_bgc_fill_rect.setAttribute("y", 0);
            // obj_bgc_fill_rect.setAttribute("width", each_rect_width);
            // obj_bgc_fill_rect.setAttribute("height", each_rect_height);
            obj_bgc_fill_rect.setAttribute("width", "100%");
            obj_bgc_fill_rect.setAttribute("height", "100%");
            obj_bgc_fill_rect.setAttribute("stroke-linecap", "round");
            // fill="rgba(255, 30, 30, 0.1)"
            obj_bgc_fill_rect.setAttribute("fill", "rgba(255, 30, 30, 0.7)");

            obj_bgc_pattern.appendChild(obj_bgc_fill_rect);
            obj_bgc_defs.appendChild(obj_bgc_pattern);
            return obj_bgc_defs;
        }


        /************************************************************************************************
         * 类型:
         *    函数
         * 功能:
         *    创建单个反向的高亮rect
         * 参数:
         *    @param { Promise<String> } obj 构造 create_bgc_highlight_rect时的参数
         * 返回:
         *    NA
         * 例子:
         *    NA
         * 备注:
         *    直接插入dom, 在obj的第一个小孩之前
         * 修改:
         *    1. 类型 : 创建
         *       作者 : 巫昭雯
         *       时间 : 2020-12-12
         *       内容 : 所有代码
        ************************************************************************************************/
        function create_loop_rect(obj) {
            if (ui_loop_num && str_dir && obj) {
                var ui_rect_x = 0;
                var ui_rect_y = 0;
                var obj_fisrt_chi = obj.firstChild;

                for (var ui_i = 1; ui_i <= ui_loop_num; ui_i++) {
                    var obj_bgc_rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");

                    obj_bgc_rect.setAttribute("class", str_rect_class);
                    obj_bgc_rect.setAttribute("width", str_rect_width);
                    obj_bgc_rect.setAttribute("height", str_rect_height);
                    obj_bgc_rect.setAttribute("fill", "url(#" + str_bgc_pat_id + ")");

                    if ("X" === str_dir) {
                        obj_bgc_rect.setAttribute("x", ui_rect_x);
                        obj_bgc_rect.setAttribute("y", (ui_step * ui_i) - str_rect_height);
                    } else {
                        obj_bgc_rect.setAttribute("x", ui_step * ui_i - str_rect_width);
                        obj_bgc_rect.setAttribute("y", ui_rect_y);
                    }

                    if (obj_fisrt_chi) {
                        obj.insertBefore(obj_bgc_rect, obj_fisrt_chi);
                        // obj.appendChild(obj_bgc_rect);
                    }
                }
            }
        }


        // **************************************** create_bgc_highlight_rect 接口 **************************************** //
        // 设置pattern id
        this.set_pattern_id = function (str) {
            if ("string" !== typeof str) {
                return false;
            }

            str_bgc_pat_id = str;

            return true;
        };


        // 设置高亮个数;
        this.set_number = function (ui_num) {
            if ("number" !== typeof ui_num) {
                return false;
            }

            ui_loop_num = ui_num;

            return true;
        };

        /************************************************************************************************
         * 类型:
         *    函数
         * 功能:
         *    设置高亮方向
         * 参数:
         *    @param { Promise<String> } str "X" === x轴方向 || "Y" === y轴方向
         * 返回:
         *    @returns { Promise<Boolean> } 参数1
         * 例子:
         *    NA
         * 备注:
         *    X轴处理高属性; Y轴处理宽属性
         *    "X" === str 该高亮条宽度为 100%
         * 修改:
         *    1. 类型 : 创建
         *       作者 : 巫昭雯
         *       时间 : 2020-12-12
         *       内容 : 所有代码
        ************************************************************************************************/
        this.set_direction = function (str) {
            if ("string" !== typeof str) {
                return false;
            }

            if ("X" === str) {
                str_dir = str;
                str_rect_class = "bgc_hl_x";
                str_rect_width = "100%";
                ui_step = 8 * each_rect_height;
            }

            if ("Y" === str) {
                str_dir = str;
                str_rect_class = "bgc_hl_y";
                str_rect_height = "100%";
                ui_step = 8 * each_rect_width;
            }

            return true;
        };

        // 创建完成
        this.create_done = function () {
            if (!obj_wrap_svg) {
                return false;
            }

            create_loop_rect(obj_wrap_svg);
            return true;
        };

        // 获取defs
        this.get_defs = function () {
            return init_defs();
        };


        return true;
    }


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    svg图层移动事件
     * 参数:
     *    @param { Promise<Object> } event 鼠标事件默认参数
     * 返回：
     *    NA
     * 例子：
     *    NA
     * 备注：
     *    点击鼠标左键：创建选中块
     *    点击鼠标右键：取消框选事件
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2019.12.17
     *      内容 : 所有代码
     ************************************************************************************************/
    // var handler = null;
    function svg_mousemove(event) {
        // console.log("[~~ move ~~]", event);

        merge_call_operation("tb_io_clear_scroll_timer");

        // 阻止事件分发
        // event.stopPropagation();

        // 定义鼠标按下释放事件
        this.onmousedown = svg_mousemove_dowm;
        this.onmouseup = svg_mousemove_up;

        b_is_enter_after_down = false;
        set_hover_div_style_mousemove(false, event);

        // 定义选中的div是否为编辑状态
        var b_is_edit_status = false;

        // 保存框选对象和虚拟div(复制)
        var obj_virtual_div = document.getElementById(str_drag_virtual_div_id);

        // ************ 定义拖拽复制数据 ************
        // 框选数据
        var obj_box_sel_data = "";
        // 虚拟div数据;
        var obj_virtual_div_data = "";
        // 复制个数;x轴或y轴
        var obj_copy_num = "";
        // ************ 拖拽复制数据 ************


        // 鼠标移动计算修改虚拟框
        var obj_data = chg_virtual_block_when_mousemove(event);

        if (obj_data) {
            obj_box_sel_data = obj_data.obj_box_sel;
            obj_virtual_div_data = obj_data.obj_copy_virtual;
            obj_copy_num = obj_data.obj_copy_num;
        }


        /************************************************************************************************
         * 类型:
         *    函数
         * 功能:
         *    svg图层鼠标按下事件
         * 参数:
         *    @param { Promise<Object> } event_down 键盘事件默认参数
         * 返回：
         *    NA
         * 例子：
         *    NA
         * 备注：
         *    点击鼠标左键：创建选中块
         *    点击鼠标右键：取消框选事件
         * 修改:
         *   1. 类型 : 创建
         *      作者 : 巫昭雯
         *      时间 : 2019.12.17
         *      内容 : 所有代码
         ************************************************************************************************/
        function svg_mousemove_dowm(event_down) {
            if (0 === event_down.button) {
                // if (prompt_page_zoom()) {
                //     return;
                // }
                // console.log("svg_mousemove_dowm");
                // 当前为编辑状态
                var x_rem = event_down.offsetX / each_rect_width;
                var y_rem = event_down.offsetY / each_rect_height;

                if (is_edit_status()) {
                    event_down.preventDefault();
                    event_down.stopPropagation();

                    b_is_edit_status = true;

                    var x_val = Math.ceil(x_rem);
                    var y_val = Math.ceil(y_rem);
                    var left = each_rect_width * (x_val - 1);
                    var top = each_rect_height * (y_val - 1);
                    var id = "edit_" + x_val + "_" + y_val;

                    create_pos_div(id, left, top);
                    add_cursor_pos_val(x_val, y_val);
                    return;
                }

                var x_rem_floor = Math.floor(x_rem);
                var y_rem_floor = Math.floor(y_rem);

                // 点击保存当前点击位置；求出当前格子的范围
                this.keydown_left_min = x_rem_floor * each_rect_width;
                this.keydown_left_max = (x_rem_floor + 1) * each_rect_width;
                this.keydown_top_min = y_rem_floor * each_rect_height;
                this.keydown_top_max = (y_rem_floor + 1) * each_rect_height;
            }

            // 按下时可以处理右键取消选中
            if (2 === event_down.button) {
                // 鼠标点击右键
                event_down.stopPropagation();
                uncheck_create_block();
            }
        }


        /************************************************************************************************
         * 类型:
         *    函数
         * 功能:
         *    svg图层鼠标释放事件
         * 参数:
         *    @param { Promise<Object> } event_up 鼠标事件默认参数
         * 返回：
         *    NA
         * 例子：
         *    NA
         * 备注：
         *    NA
         * 修改:
         *   1. 类型 : 创建
         *      作者 : 巫昭雯
         *      时间 : 2019.12.17
         *      内容 : 所有代码
         ************************************************************************************************/
        function svg_mousemove_up(event_up) {
            if (0 === event_up.button) {
                // console.log("svg_mousemove_up=====");

                if (!m_this.b_is_mousedown) {
                    // m_this.b_is_mousedown = false;
                    // return;
                }

                // 编辑选中；当前为点击选中位置
                if (b_is_edit_status) {
                    event_up.stopPropagation();
                    return;
                }

                // 鼠标释放；
                if (event_up.target) {
                    $("#" + mc_svg_id + " .click_create_div").css("pointer-events", "auto");
                    b_is_enter_after_down = false;

                    if (obj_virtual_div) {
                        // 拖拽完毕；复制算法；

                        if (event_up.currentTarget !== this) {
                            // 外部释放
                            obj_copy_num = get_drag_copy_num();
                            obj_virtual_div_data = {
                                width: obj_virtual_div.offsetWidth,
                                height: obj_virtual_div.offsetHeight,
                                left: obj_virtual_div.offsetLeft,
                                top: obj_virtual_div.offsetTop
                            };
                        }

                        copy_block_after_drag(obj_virtual_div, obj_box_sel_data, obj_virtual_div_data, obj_copy_num);
                    } else {
                        // 判断不是拖拽
                        var b_no_drag_left = this.keydown_left_min <= event_up.offsetX && event_up.offsetX <= this.keydown_left_max;
                        var b_no_drag_top = this.keydown_top_min <= event_up.offsetY && event_up.offsetY <= this.keydown_top_max;

                        if (b_no_drag_left && b_no_drag_top) {
                            // 点击之后没有拖拽+
                            // if (Number.isNaN(obj_external_mod_data.ui_single_sweep_len)) {
                            if ("undefined" === obj_external_mod_data.ui_single_sweep_len) {
                                show_info_massage("MC_LANG_POPOUT_SCAN_LEN_VAL_ERROR");
                                return;
                            }

                            //将所有的生成点取消掉编辑状态、
                            $("#" + mc_svg_id + " .click_create_div").attr("contenteditable", false);

                            // 没有拖拽直接生成
                            if (!b_is_virtual_point_mode) {
                                ui_click_num++;
                                // var g_ui_click_cnt = get_g_click_cnt();
                            }

                            ui_click_num_virtual++;
                            var id = "click_div_" + ui_click_num_virtual;

                            // 点击生成
                            remove_selected_div_class();
                            create_div_mouse_click(this.keydown_top_min, this.keydown_left_min, get_g_max_val() + 1, id, "click_create_div");
                            set_style_after_drag(event_up, ui_selected_xy);
                            // mc_svg_tb_hardware_callback(2,get_next_dot_data());
                            mc_svg_tb_hardware_callback(2, true);
                            return;
                        }
                    }

                    // 生成块鼠标移动事件在svg滑动时需要框选之前已选中的div
                    if (0 !== arr_create_obj.length) {
                        set_style_after_drag("键盘事件不存在生成块", ui_selected_xy);
                    }
                }
            }
        }
    }


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    拖拽完毕；复制算法; 并生成复制块
     * 参数:
     *    @param { Promise<Object> } obj_vblock 复制虚拟div块
     *    @param { Promise<Object> } obj_box_sel_rect 框选矩形数据
     *    @param { Promise<Object> } obj_virtual_rect 虚拟块矩形数据(拖拽复制块)
     *    @param { Promise<Object> } obj_copy_num 复制个数 { X:X轴复制个数, Y:Y轴复制个数 }
     * 返回:
     *    NA
     * 例子:
     *    NA
     * 备注:
     *    矩形数据参数属性值为 { width, height, left, top }
     * 修改:
     *    1. 类型 : 创建
     *       作者 : 巫昭雯
     *       时间 : 2020-12-10
     *       内容 : 所有代码
    ************************************************************************************************/
    function copy_block_after_drag(obj_vblock, obj_box_sel_rect, obj_virtual_rect, obj_copy_num) {
        if (!obj_vblock) {
            return;
        }

        if (!obj_box_sel_rect || !obj_virtual_rect || !obj_copy_num) {
            // 移除虚拟div
            obj_vblock.parentElement.removeChild(obj_vblock);
            return;
        }

        // 定义框选方向是否为x轴
        var box_select_dir = true;
        // 定义拖拽方向是否为x轴
        var drag_dir = true;
        // 复制个数
        var ui_copy_num_x = obj_copy_num.X;
        var ui_copy_num_y = obj_copy_num.Y;

        if (0 === ui_copy_num_x && 0 === ui_copy_num_y) {
            // 移除虚拟div
            obj_vblock.parentElement.removeChild(obj_vblock);
            return;
        }

        // x轴
        if (obj_box_sel_rect.height === obj_virtual_rect.height) {
            drag_dir = false;
        }

        if (obj_box_sel_rect.height === each_rect_height) {
            box_select_dir = false;
        }

        // ============= 定义对象继承参数数据 =============
        // 框选数据；
        var box_select_data = obj_box_sel_rect;

        box_select_data.dir = box_select_dir;

        // 虚拟框选div数据
        var virtual_div_data = obj_virtual_rect;

        virtual_div_data.dir = drag_dir;

        // 移除框选位置的div
        remove_box_select_div(box_select_data, virtual_div_data);

        // ============= 计算 =============
        var box_select_obj_len = arr_box_select_obj.length;
        // 框选方向和复制方向相同时
        var arr_sort_same_dir = [];

        if (box_select_dir === drag_dir) {
            // 定义相同方向下复制组的排序数组
            arr_sort_same_dir = set_gp_num(box_select_obj_len, ui_copy_num_x);

            if (drag_dir) {
                arr_sort_same_dir = set_gp_num(box_select_obj_len, ui_copy_num_y);
            }
        }

        // 清空
        arr_block_data_change_sub = [];

        // 两个方向相等时：执行分割复制动作
        for (var create_idx = 0; create_idx < box_select_obj_len; create_idx++) {
            var create_item = arr_box_select_obj[create_idx];
            var item_top = create_item.offsetTop;
            var item_left = create_item.offsetLeft;
            var item_init_fml = create_item.getAttribute("init_fml");
            var item_formula = create_item.getAttribute("formula");
            var item_sweep = create_item.getAttribute("sweep");
            var item_point = create_item.getAttribute("point");

            // 复制数据
            var loop_num = 0;

            // 同方向
            if (box_select_dir === drag_dir) {
                loop_num = arr_sort_same_dir[create_idx];
            } else {
                if (drag_dir) {
                    loop_num = ui_copy_num_y;
                } else {
                    loop_num = ui_copy_num_x;
                }
            }

            // 创建的div项位置数据;需要变动的位置数据；
            var obj_item_pos_data = {
                top: item_top,
                left: item_left
            };

            // 循环数据
            var loop_data = {
                loop_num: loop_num,
                init_fml: item_init_fml,
                formula: item_formula,
                val: item_formula,
                sweep: item_sweep,
                point: item_point
            };

            drag_copy_singel_block(box_select_data, virtual_div_data, obj_item_pos_data, loop_data);
        }

        // push入栈
        multiple_data_val_chg();
        // 更新队列
        update_formula_map_val();
        // 移除虚拟div
        obj_vblock.parentElement.removeChild(obj_vblock);
    }


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    鼠标移出svg;
     * 参数:
     *    @param { Promise<Object> } event 鼠标事件参数对象
     * 返回：
     *    NA
     * 例子：
     *    NA
     * 备注：
     *    NA
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2020.4.2
     *      内容 : 所有代码
     ************************************************************************************************/
    function svg_mouseout(event) {
        event.preventDefault();
        event.stopPropagation();

        // console.log("[鼠标移出]", event);
        // console.log("[ ********************************* 移出 *********************************]");

        var $obj_virtual_div = $("#" + str_drag_virtual_div_id);

        if (0 !== $obj_virtual_div.length) {
            // $obj_virtual_div.remove();

            var str_dir = get_mouseout_direction(event);

            if (str_dir) {
                // 控制滚动条
                // copy_drag_when_mouseout(event, str_dir);
            }
        }
        return;
    }


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    鼠标在外部移动后续操作
     * 参数:
     *    @param { Promise<String> } event 鼠标移动事件
     * 返回:
     *    NA
     * 例子:
     *    NA
     * 备注:
     *    转化鼠标位置值为svg的相对位置值 -> 计算复制个数
     * 修改:
     *    1. 类型 : 创建
     *       作者 : 巫昭雯
     *       时间 : 2020-12-14
     *       内容 : 所有代码
    ************************************************************************************************/
    this.mousemove_on_doc = function (event) {
        if (event) {
            change_doc_pos_to_inner_pos(event);
            return;
        }

        m_this.tb_io_clear_scroll_timer();
    };


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    将外部鼠标位置值转化为内部 位置值
     * 参数:
     *    @param { Promise<String> } event 鼠标移动事件
     * 返回:
     *    NA
     * 例子:
     *    NA
     * 备注:
     *    NA
     * 修改:
     *    1. 类型 : 创建
     *       作者 : 巫昭雯
     *       时间 : 2020-12-14
     *       内容 : 所有代码
    ************************************************************************************************/
    function change_doc_pos_to_inner_pos(event) {
        if (event) {
            // 转化后的值(转化为相对svg基准点offset值) === 鼠标值 - 最外层为值 - 偏差 30 + 混动条值
            var ui_this_x = event.clientX - obj_form_box.offsetLeft - 30 + obj_scroll_wrap.scrollLeft;
            var ui_this_y = event.clientY - obj_form_box.offsetTop - 30 + obj_scroll_wrap.scrollTop;

            // console.log("[doc 移动 最外层位置]", obj_form_box.offsetLeft, obj_form_box.offsetTop, "[滚动条值]", obj_scroll_wrap.scrollLeft, obj_scroll_wrap.scrollTop);
            // console.log("[外部转化后的值]", ui_this_x, ui_this_y );

            // 边界处理
            var res_xy = handle_drag_copy_boundary_values(ui_this_x, ui_this_y);

            if (res_xy) {
                // console.log("边界处理值",res_xy.X, res_xy.Y);

                // 移动计算参数
                var obj_event = {
                    offsetX: res_xy.X,
                    offsetY: res_xy.Y
                };

                var str_dir = get_mouseout_direction(obj_event);

                if (str_dir) {
                    m_this.tb_io_clear_scroll_timer();

                    chg_virtual_block_when_mousemove(obj_event);
                    copy_drag_when_mouseout(obj_event, str_dir);
                }
            }
        }
    }


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    获取鼠标移出方向; 获取鼠标当前位置的方向(使用offsetXY值)
     * 参数:
     *    @param { Promise<Object> } event 鼠标移出时event参数; 主要使用 offset值
     * 返回:
     *    @returns { Promise<String> } 返回移出的方向; "L" || "R" || "T" || "B" || false === 鼠标没有移出(或参数错误)
     * 例子:
     *    NA
     * 备注:
     *    "L" === "left" === 左
     *    "R" === "right" === 右
     *    "T" === "top" === 上
     *    "B" === "botton" === 下
     * 修改:
     *    1. 类型 : 创建
     *       作者 : 巫昭雯
     *       时间 : 2020-12-12
     *       内容 : 所有代码
    ************************************************************************************************/
    function get_mouseout_direction(event) {
        if (event) {
            // obj_scroll_wrap client = view wh
            if (event.offsetX <= obj_scroll_wrap.scrollLeft) {
                return "L";
            }

            if (event.offsetX >= obj_scroll_wrap.scrollLeft + obj_scroll_wrap.clientWidth) {
                return "R";
            }

            if (event.offsetY <= obj_scroll_wrap.scrollTop) {
                return "T";
            }

            if (event.offsetY >= obj_scroll_wrap.scrollTop + obj_scroll_wrap.clientHeight) {
                return "B";
            }

            return false;
        }

        return false;
    }


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    鼠标拖拽移出框外操作；控制滚动条移动 + 增加复制框虚拟框
     * 参数:
     *    @param { Promise<String> } event 鼠标移出时event
     *    @param { Promise<String> } str_dir 鼠标移出方向; 与 get_mouseout_direction 返回参数一致
     * 返回:
     *    NA
     * 例子:
     *    NA
     * 备注:
     *    NA
     * 修改:
     *    1. 类型 : 创建
     *       作者 : 巫昭雯
     *       时间 : 2020-12-12
     *       内容 : 所有代码
    ************************************************************************************************/
    function copy_drag_when_mouseout(event, str_dir) {
        var obj_ev = event;

        if ("object" === typeof obj_ev && str_dir) {
            var obj_event = {
                offsetX: event.offsetX,
                offsetY: event.offsetY
            };

            var str_scroll_dir = "";
            var str_offset_dir = "";
            var ui_step_copy = 0;
            // x 方向
            var ui_x_dir = 0;

            if ("L" === str_dir || "R" === str_dir) {
                str_scroll_dir = "scrollLeft";
                str_offset_dir = "offsetX";
                ui_step_copy = each_rect_width;
                ui_x_dir = 1;
            } else if ("T" === str_dir || "B" === str_dir) {
                str_scroll_dir = "scrollTop";
                str_offset_dir = "offsetY";
                ui_step_copy = each_rect_height;
            }

            // ui_step_copy = 10;
            var ui_step_scroll = ui_step_copy + 2;
            var ui_add = 1;

            if ("L" === str_dir || "T" === str_dir) {
                ui_add = 0;
            } else if ("R" === str_dir || "B" === str_dir) {
                ui_add = 1;
            }

            m_this.tb_io_clear_scroll_timer();
            // var ui_num = 0;

            // 滚动 + 延长复制框
            m_ui_timer = setInterval(function () {
                // console.log("定时器", ui_num );
                // ui_num++;

                if (ui_add) {
                    obj_scroll_wrap[str_scroll_dir] += ui_step_scroll;
                } else {
                    obj_scroll_wrap[str_scroll_dir] -= ui_step_scroll;
                }

                m_this.update_scroll();

                // 边界处理
                var b_res = handle_drag_copy_boundary_values(obj_event.offsetX, obj_event.offsetY);

                if (b_res) {
                    // 停止
                    var b_x_over = ui_x_dir && b_res.b_over_x;
                    var b_y_over = 0 === ui_x_dir && b_res.b_over_y;

                    if (b_x_over || b_y_over) {
                        obj_event = {
                            offsetX: b_res.X,
                            offsetY: b_res.Y
                        };
                        chg_virtual_block_when_mousemove(obj_event);
                        merge_call_operation("tb_io_clear_scroll_timer");
                        return;
                    }
                }

                chg_virtual_block_when_mousemove(obj_event);

                if (ui_add) {
                    obj_event[str_offset_dir] += ui_step_copy;
                } else {
                    obj_event[str_offset_dir] -= ui_step_copy;
                }
            }, 80);
        }
    }


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    处理拖拽复制边界值; 若触碰到边界则设置滚动条到边界
     * 参数:
     *    @param { Promise<Number> } ui_x 鼠标事件offsetX值
     *    @param { Promise<Number> } ui_y 鼠标事件offsetY值
     * 返回:
     *    @returns { Promise<Object> } 返回处理后的值; 若为超出边界则返回原值 || 边界值
     * 例子:
     *    NA
     * 备注:
     *    返回参数属性:
     *      1. b_over_x 是否超出边界X
     *      1. b_over_y 是否超出边界Y
     *      2. X 返回处理后的offsetX值
     *      3. Y 返回处理后的offsetY值
     * 修改:
     *    1. 类型 : 创建
     *       作者 : 巫昭雯
     *       时间 : 2020-12-14
     *       内容 : 所有代码
    ************************************************************************************************/
    function handle_drag_copy_boundary_values(ui_x, ui_y) {
        if ("undefined" === typeof ui_x || "undefined" === typeof ui_y) {
            return false;
        }

        // scrollWidth
        var ui_max_x = obj_scroll_wrap.scrollWidth;
        var ui_max_y = obj_scroll_wrap.scrollHeight;
        var b_over_x = false;
        var b_over_y = false;

        // 边界处理
        if (0 >= ui_x) {
            ui_x = 0;
            obj_scroll_wrap.scrollLeft = 0;
            b_over_x = true;
        }

        if (0 >= ui_y) {
            ui_y = 0;
            obj_scroll_wrap.scrollTop = 0;
            b_over_y = true;
        }

        if (ui_x >= ui_max_x) {
            ui_x = ui_max_x;
            obj_scroll_wrap.scrollLeft = ui_x;
            b_over_x = true;
        }

        if (ui_y >= ui_max_y) {
            ui_y = ui_max_y;
            obj_scroll_wrap.scrollTop = ui_y;
            b_over_y = true;
        }

        return {
            b_over_x: b_over_x,
            b_over_y: b_over_y,
            X: ui_x,
            Y: ui_y
        };
    }


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    1. 鼠标移动时 --> 修改虚拟块大小位置;
     *    2. 当复制拖拽鼠标移出框外(在框外鼠标移动时) -> 改变虚拟复制块
     * 参数:
     *    @param { Promise<Object> } event 鼠标移动事件参数 || 带offsetXY的属性对象
     * 返回:
     *    @returns { Promise<Object> } 移动时修改的虚拟块数据 && 复制的X轴Y轴的个数 || false
     * 例子:
     *    NA
     * 备注:
     *    返回对象属性解释：
     *      1. obj_box_sel === 框选块数据; {width,height,top,left}
     *      1. obj_copy_virtual === 复制虚拟框数据; {width,height,top,left}
     *      1. obj_copy_num === X轴和Y轴的复制个数; {X,Y}
     * 修改:
     *    1. 类型 : 创建
     *       作者 : 巫昭雯
     *       时间 : 2020-12-12
     *       内容 : 所有代码
    ************************************************************************************************/
    function chg_virtual_block_when_mousemove(event) {
        if (!event) {
            return false;
        }

        if ("object" !== typeof event) {
            return false;
        }

        // 保存框选对象和虚拟div(复制)
        var obj_virtual_div = document.getElementById(str_drag_virtual_div_id);
        var obj_box_selete_div = document.getElementById(box_selection_wrap_id);

        // ************ 定义拖拽复制数据 ************
        // 框选数据
        var box_selete_left = "";
        var box_selete_top = "";
        var box_selete_width = "";
        var box_selete_height = "";
        // 框选数据中心点位置
        var box_selete_center_left = "";
        var box_selete_center_top = "";
        // 框选边界位置值
        var box_selete_max_left = "";
        var box_selete_max_top = "";

        // 设置虚拟div数据
        var virtual_left = "";
        var virtual_top = "";
        var virtual_width = "";
        var virtual_height = "";

        // 复制个数;x轴或y轴
        var copy_x_num = 0;
        var copy_y_num = 0;
        // ************ 拖拽复制数据 ************

        // 拖拽复制
        if (obj_virtual_div && obj_box_selete_div) {
            // 框选数据
            box_selete_left = obj_box_selete_div.offsetLeft;
            box_selete_top = obj_box_selete_div.offsetTop;
            box_selete_width = obj_box_selete_div.offsetWidth;
            box_selete_height = obj_box_selete_div.offsetHeight;
            box_selete_center_left = box_selete_width / 2 + box_selete_left;
            box_selete_center_top = box_selete_height / 2 + box_selete_top;
            box_selete_max_left = box_selete_left + box_selete_width;
            box_selete_max_top = box_selete_top + box_selete_height;

            // 初始化虚拟div
            // obj_virtual_div.css({"top": box_selete_top,"left": box_selete_left,"width": box_selete_width,"height": box_selete_height});

            // 初始化虚拟div
            obj_virtual_div.style.cssText += "width:" + box_selete_width + "px;height:" + box_selete_height + "px;top:" + box_selete_top + "px;left:" + box_selete_left + "px;";


            // 定义否需要计算
            var b_can_calc = true;

            // 在框选范围之内
            if (Number(box_selete_left) < Number(event.offsetX) && Number(event.offsetX) < Number(box_selete_max_left) && Number(box_selete_top) < Number(event.offsetY) && Number(event.offsetY) < Number(box_selete_max_top)) {
                b_can_calc = false;
            }


            // 计算
            if (b_can_calc) {
                // 位置离中心点得距离
                var dis_left = event.offsetX - box_selete_center_left;
                var dis_top = event.offsetY - box_selete_center_top;


                // 位置差值;右下位置差
                var dif_left = Math.abs(dis_left) - (box_selete_width / 2);
                var dif_top = Math.abs(dis_top) - (box_selete_height / 2);

                // 位置差个数
                var dif_left_num = Math.round(Math.abs(dif_left) / each_rect_width);
                var dif_top_num = Math.round(Math.abs(dif_top) / each_rect_height);

                // left值在框选范围内不做判断
                if (Number(box_selete_left) < Number(event.offsetX) && Number(event.offsetX) < Number(box_selete_max_left)) {
                    dif_left_num = 0;
                }

                // top值在框选范围内不做判断
                if (Number(box_selete_top) < Number(event.offsetY) && Number(event.offsetY) < Number(box_selete_max_top)) {
                    dif_top_num = 0;
                }

                // 设置虚拟div数据
                virtual_left = box_selete_left;
                virtual_top = box_selete_top;
                virtual_width = each_rect_width * dif_left_num + box_selete_width;
                virtual_height = each_rect_height * dif_top_num + box_selete_height;


                // 单个方向；只能拖动x轴或者是y轴
                if (dif_left_num > dif_top_num) {
                    // 往左
                    if (0 > dis_left) {
                        virtual_left = box_selete_left - each_rect_width * dif_left_num;
                    }
                    virtual_height = box_selete_height;
                } else {
                    // 往上
                    if (0 > dis_top) {
                        virtual_top = box_selete_top - each_rect_height * dif_top_num;
                    }
                    virtual_width = box_selete_width;
                }

                // obj_virtual_div.css({"top": virtual_top,"left": virtual_left,"width": virtual_width,"height": virtual_height });

                // 设置框选框样式
                obj_virtual_div.style.cssText += "width:" + virtual_width + "px;height:" + virtual_height + "px;top:" + virtual_top + "px;left:" + virtual_left + "px;";

                // x,y轴复制个数
                copy_x_num = dif_left_num;
                copy_y_num = dif_top_num;
            }
        }


        // 框选数据；
        var obj_box_sel_data = {
            width: box_selete_width,
            height: box_selete_height,
            left: box_selete_left,
            top: box_selete_top
        };

        // 虚拟框选div数据
        var obj_virtual_div_data = {
            width: virtual_width,
            height: virtual_height,
            left: virtual_left,
            top: virtual_top
        };

        // 复制个数
        var obj_copy_num = {
            X: copy_x_num,
            Y: copy_y_num
        };

        return {
            obj_box_sel: obj_box_sel_data,
            obj_copy_virtual: obj_virtual_div_data,
            obj_copy_num: obj_copy_num
        };
    }


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    获取拖拽复制个数; 当存在拖拽复制时 && 鼠标在外部释放时使用
     * 参数:
      *    NA
     * 返回:
     *    @returns { Promise<Object> } X轴和Y轴方向的个数 { X === X轴复制个数, Y === Y轴复制个数  }
     * 例子:
     *    NA
     * 备注:
     *    NA
     * 修改:
     *    1. 类型 : 创建
     *       作者 : 巫昭雯
     *       时间 : 2020-12-12
     *       内容 : 所有代码
    ************************************************************************************************/
    function get_drag_copy_num() {
        var obj_virtual_div = document.getElementById(str_drag_virtual_div_id);
        // obj_box_selection_wrap_div
        var obj_box_selete_div = obj_box_selection_wrap_div;

        var ui_x = 0;
        var ui_y = 0;

        if (!obj_virtual_div || !obj_box_selete_div) {
            return {
                X: ui_x,
                Y: ui_y
            };
        }

        var ui_virtual_w = obj_virtual_div.offsetWidth;
        var ui_virtual_h = obj_virtual_div.offsetHeight;

        var ui_box_w = obj_box_selete_div.offsetWidth;
        var ui_box_h = obj_box_selete_div.offsetHeight;

        if (ui_virtual_w !== ui_box_w) {
            ui_x = Math.floor((ui_virtual_w - ui_box_w) / each_rect_width);
        }

        if (ui_virtual_h !== ui_box_h) {
            ui_y = Math.floor((ui_virtual_h - ui_box_h) / each_rect_height);
        }

        return {
            X: ui_x,
            Y: ui_y
        };
    }


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    创建鼠标事件的div
     * 参数:
     *    NA
     * 返回：
     *    NA
     * 例子：
     *    NA
     * 备注：
     *    鼠标移入时的div;
     *    框选事件时的div;
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2019.12.17
     *      内容 : 所有代码
     ************************************************************************************************/
    function create_div_mouse_event() {
        hover_content_id = id_name + "_div_hover_content";
        hover_head_id = id_name + "_div_hover_head";
        hover_nav_id = id_name + "_div_hover_nav";
        box_selection_wrap_id = id_name + "_box_selection_wrap_div";
        small_div_id = id_name + "_small_div";

        // 鼠标移动时的div
        var str_hover_content_div = "<div id='" + hover_content_id + "' class='div_hover_content' style='width:" + each_rect_width + "px;height:" + each_rect_height + "px'></div>";
        var str_hover_head_div = "<div id='" + hover_head_id + "' class='div_hover_head' style='width:" + each_rect_width + "px;height:" + 30 + "px'></div>";
        var str_hover_nav_div = "<div id='" + hover_nav_id + "' class='div_hover_nav' style='width:" + 30 + "px;height:" + each_rect_height + "px'></div>";
        // 生成框选外层div
        var str_boxsele_wrap_div = "<div id='" + box_selection_wrap_id + "' class='box_selection_wrap_div' style=''><div id='" + small_div_id + "' class='small_div' style=''></div></div>";

        $obj_svg_jq.append(str_hover_content_div + str_boxsele_wrap_div);

        $("#" + mc_nav_wrap_id + " .mc_svg_nav").append(str_hover_nav_div);
        $("#" + mc_head_wrap_id + " .mc_svg_head").append(str_hover_head_div);

        // 保存dom对象
        obj_small_div = document.getElementById(small_div_id);
        obj_box_selection_wrap_div = document.getElementById(box_selection_wrap_id);
        obj_div_hover_content = document.getElementById(hover_content_id);
        obj_div_hover_head = document.getElementById(hover_head_id);
        obj_div_hover_nav = document.getElementById(hover_nav_id);

        if (10 === ui_zha_ie_version) {
            obj_div_hover_content.style.zIndex = -1;
            // obj_div_hover_content.addEventListener("mousemove", function () {
            // console.log("渣ie" + ui_zha_ie_version);
            // });
            // obj_div_hover_content.addEventListener("mousedown", function () {
            //     console.log("渣ie" + ui_zha_ie_version + "点击");
            // });
        }


        // obj_div_hover_content.addEventListener("mousedown", function () {
        //     console.log("渣ie" + ui_zha_ie_version + "点击");

        //     handler = function (e) {
        //         e = e || window.event;
        //         var target = e.target || e.srcElement;

        //         if (hover_content_id === target.id) {
        //             if (!e.preventDefault) {
        //                 //IE quirks
        //                 e.returnValue = false;
        //                 e.cancelBubble = true;
        //             }
        //             e.preventDefault();
        //             e.stopPropagation();
        //         }
        //     };


        //     // if (window.addEventListener) {
        //     //     svg_item_content.addEventListener("mousedown", handler, false);
        //     // } else {
        //     //     svg_item_content.attachEvent("onmousedown", handler);
        //     // }
        // });


        // 框选块添加事件监听
        obj_small_div.addEventListener("mousedown", small_div_mousedown);
        // obj_box_selection_wrap_div.addEventListener("mousedown", boxsele_wrap_div_mousedown, true);
    }


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    创建鼠标点击时的生成的div（鼠标点击生成）
     * 参数:
     *     @param { Promise<String> } top 生成div的top值;
     *     @param { Promise<String> } left 生成div的left值;
     *     @param { Promise<String> } val 生成div的value值;
     *     @param { Promise<String> } id 生成div的id值;
     * 返回：
     *    NA
     * 例子：
     *    NA
     * 备注：
     *    并给生成的div创建监听事件
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2019.12.17
     *      内容 : 所有代码
     ************************************************************************************************/
    function create_div_mouse_click(top, left, val, id) {
        var real_id = id_name + "_" + id + "_" + (Math.random() * 1000).toFixed();
        // var is_edit = false;
        // var b_choosr = true;
        var x_val = left / each_rect_width + 1;
        var y_val = top / each_rect_height + 1;
        var formula = val;
        var init_fml = val;
        var pos_val = "(" + x_val + ":" + y_val + ")";
        var sweep = ui_sweep_num;
        var point = ui_virtual_point;
        var str_div_type = get_div_type_val(0);
        var str_class_name = "click_create_div  box_selection_creat_div ";


        // 虚点走点模式
        if (b_is_virtual_point_mode) {
            real_id += "_virtual_point";
            formula = "";
            init_fml = "";
            pos_val = "";
            point = 1;
            val = "[" + ui_sweep_num + "]";
            str_div_type = get_div_type_val(3);
            str_class_name += " click_create_div_virtual_point";
        }

        // 第几组数据
        // var grp_idx = Math.ceil(formula / obj_external_mod_data.ui_single_grp_num);

        var str_click_div = "<div id='" + real_id + "' div_type='" + str_div_type + "' is_choose='true' tabindex='0' contenteditable='false' class='" + str_class_name + "' style='top:" + top + "px;left:" + left + "px;width:" + each_rect_width + "px;height:" + each_rect_height +
            "px;line-height:" + each_rect_height + "px;' x='" + x_val + "' y='" + y_val + "' pos='" + pos_val + "' init_fml = '" + init_fml + "'  formula='" + formula +
            "'sweep='" + sweep + "' point='" + point + "'>" + val + "</div>";

        $obj_svg_jq.append(str_click_div);

        var obj_div_click = document.getElementById(real_id);

        operation_of_new_create_point(obj_div_click);

        // 更新位置对象
        obj_pos_formula[pos_val] = formula;
        // 设置生成之后就可以获得焦点；可触发键盘事件
        obj_div_click.focus();
        // 生成div的同时保存框选对象
        arr_box_select_obj = [obj_div_click];

        set_edit_box_val(formula);
        set_hover_div_val(0);
        update_formula_map_val();

        var param_data = {
            b_is_init: true,
            pos: pos_val,
            init_fml: init_fml,
            formula: formula,
            point: point,
            sweep: sweep,
            id: real_id
        };

        // 入栈
        set_val_change_action(param_data, multiple_data_val_chg);
    }


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    新创建点的操作; 统一处理; 包括：添加事件监听 && 加入创建点集合 && 绑定数据变动函数
     * 参数:
     *    @param { Promise<String> } obj_point
     * 返回:
     *    NA
     * 例子:
     *    NA
     * 备注:
     *    NA
     * 修改:
     *    1. 类型 : 创建
     *       作者 : 巫昭雯
     *       时间 : 2021-02-07
     *       内容 : 所有代码
    ************************************************************************************************/
    function operation_of_new_create_point(obj_point) {
        if ("object" !== typeof obj_point) {
            return;
        }

        if ("function" !== typeof obj_point.addEventListener) {
            return;
        }

        // 给生成的div添加事件监听
        obj_point.addEventListener("mouseenter", create_div_mouseenter);
        obj_point.addEventListener("keydown", create_div_keydown);
        obj_point.addEventListener("dblclick", create_div_dblclick);
        // 绑定数据变动函数;
        obj_point.on_val_chg = create_div_on_val_change;
        // 保存生成对象
        arr_create_obj.push(obj_point);
    }


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    添加多个事件监听
     * 参数:
     *    @param { Promise<Object> } obj 添加事件监听对象
     *    @param { Promise<Array> } arr 多个事件监听数组
     * 返回：
     *    NA
     * 例子：
     *    NA
     * 备注：
     *    该函数不接受添加的时间事件监听不接收参数
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2019.5.20
     *      内容 : 所有代码
     ************************************************************************************************/
    function add_eventListener_multiple_fun(obj, arr) {
        if (obj && arr instanceof Array) {
            arr.forEach(function (item) {
                if ("object" === typeof item && item.constructor === obj_event_format && 0 !== Object.keys(item).length) {
                    obj.addEventListener(item.event_name, item.event_fun, item.event_bubble);
                }
            });
        }
    }


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    获取计算后的行扫数和公式值
     * 参数:
     *    @param { Promise<String> } val 计算值 （String） || Number
     * 返回：
     *    @returns { Promise<Object> } 行扫数，公式，显示值；
     * 例子：
     *    NA
     * 备注：
     *    NA
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2020.5.5
     *      内容 : 所有代码
     ************************************************************************************************/
    function get_calc_sweep_formula_val(val) {
        if ("undefined" !== typeof val) {
            if ("string" !== typeof val) {
                val = val.toString();
            }

            if ("" !== val.trim()) {
                var calc_val = val;

                // 根据一组数据个数计算
                if (0 === val % obj_external_mod_data.ui_single_grp_num) {
                    calc_val = obj_external_mod_data.ui_single_grp_num;
                } else {
                    calc_val = val % obj_external_mod_data.ui_single_grp_num;
                }

                var result_mod = calc_val % obj_external_mod_data.ui_single_sweep_len;
                var result_rem = Math.floor(calc_val / obj_external_mod_data.ui_single_sweep_len);

                var re_sweep = result_rem + 1;
                var re_formula = result_mod;

                if (0 === result_mod) {
                    re_sweep = result_rem;
                    re_formula = obj_external_mod_data.ui_single_sweep_len;
                }

                var result_show = re_sweep + "_" + re_formula;
                // console.log(result_show);

                return {
                    val: result_show,
                    sweep: re_sweep,
                    formula: re_formula
                };
            }
        }

        return false;
    }


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    获取单个点的应用数据格式；获取新生成的点
     * 参数:
     *    NA
     * 返回：
     *    @returns { Promise<Object> } 数据格式对象 || 空对象 === 找不到该点
     * 例子：
     *    NA
     * 备注：
     *    NA
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2020.4.15
     *      内容 : 所有代码
     ************************************************************************************************/
    function get_next_dot_data() {// eslint-disable-line
        var arr_obj = arr_create_obj;
        var arr_len = arr_obj.length;
        var obj_last = arr_obj[arr_len - 1];

        var x_val = Number(obj_last.getAttribute("x"));
        var y_val = Number(obj_last.getAttribute("y"));
        var point = Number(obj_last.getAttribute("point"));
        var sweep = Number(obj_last.getAttribute("sweep"));
        var val = obj_last.innerText;
        var arr_int = val.match(cui_reg_int);
        var real_val = val.split("_")[1];

        if (arr_int) {
            if (0 !== point) {
                real_val = arr_int[1];
            }

            sweep = Number(arr_int[0]);

            var obj_item_data = {
                idx: real_val,
                sweep: sweep,
                x: x_val,
                y: y_val
            };

            return obj_item_data;
        }
        return {};
    }


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    生成div的值改变函数
     * 参数:
     *    @param { Promise<String> } id 需要该改变的div id
     *    @param { Promise<String> } old_val 旧的div值
     *    @param { Promise<String> } new_val 新的div值
     *    @param { Promise<String> } formula 新的公式值
     * 返回：
     *    NA
     * 例子：
     *    NA
     * 备注：
     *    NA
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2020.4.2
     *      内容 : 所有代码
     ************************************************************************************************/
    function create_div_on_val_change(id, old_val, new_val, formula) {
        var obj_div = document.getElementById(id);
        var pos_val = obj_div.getAttribute("pos");
        var grp = Number(obj_div.getAttribute("group_idx"));
        var grp_idx = Math.ceil(new_val / obj_external_mod_data.ui_single_grp_num);

        if (grp !== grp_idx) {
            // obj_div.setAttribute("group_idx",grp_idx);
        }

        // var result = get_calc_sweep_formula_val(formula);

        // 设置值改变队列
        var old_formula = obj_div.getAttribute("formula");
        var point = obj_div.getAttribute("point");
        var sweep = obj_div.getAttribute("sweep");

        // 公式不相等才堆栈
        if (compare_formula_values(old_formula, formula)) {
            var param_data = {
                b_is_init: false,
                pos: pos_val,
                init_fml: formula,
                formula: formula,
                point: point,
                sweep: sweep,
                id: id
            };

            set_val_change_action(param_data, multiple_data_val_chg);
            // remove_obj_grp_css_class(obj_div);
        }


        obj_div.setAttribute("formula", formula);
        obj_div.setAttribute("init_fml", formula);
        // obj_div.setAttribute("sweep",formula);

        if (old_val !== new_val) {
            obj_div.innerText = new_val;
        }

        // 更新
        obj_pos_formula[pos_val] = formula;
    }


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    比较公式值
     * 参数:
     *    @param { Promise<String> } old_val 旧的公式值
     *    @param { Promise<String> } new_val 新的公式值
     * 返回：
     *    @returns { Promise<Boolean> }  两个公式值相等 === false || 两个公式值不相等 === true
     * 例子：
     *    NA
     * 备注：
     *    两个公式值相等去掉等号之后比较
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2020.4.2
     *      内容 : 所有代码
     ************************************************************************************************/
    function compare_formula_values(old_val, new_val) {
        if (-1 !== old_val.indexOf("=")) {
            old_val = old_val.split("=")[1];
        }

        if ("string" !== typeof new_val) {
            new_val = new_val.toString();
        }

        var juged_formula = new_val;

        if (-1 !== new_val.indexOf("=")) {
            juged_formula = juged_formula.split("=")[1];
        }

        // 判断去掉等号的公式是否相等
        if (old_val !== juged_formula) {
            return true;
        }

        return false;
    }


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    移除选中和框选块的类
     * 参数:
     *    NA
     * 返回：
     *    NA
     * 例子：
     *    NA
     * 备注：
     *    NA
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2019.12.17
     *      内容 : 所有代码
     ************************************************************************************************/
    function remove_selected_div_class() {
        var arr_create_div = arr_create_obj;
        var arr_create_div_len = arr_create_div.length;

        if (0 !== arr_create_div_len) {
            for (var idx = 0; idx < arr_create_div_len; idx++) {
                // var class_list = arr_create_div[idx].classList;

                arr_create_div[idx].classList.remove("box_selection_creat_div");
            }
        }
    }

    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    滚动条事件
     * 参数:
     *    NA
     * 返回：
     *    NA
     * 例子：
     *    NA
     * 备注：
     *    scroll_callback(); 控制联动滚动条的回调事件
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2020.4.3
     *      内容 : 所有代码
     ************************************************************************************************/
    function scroll_event() {
        set_scroll_pos(mc_main_wrap_id, this.scrollLeft, this.scrollTop);

        if ("function" === typeof scroll_callback) {
            scroll_callback(this);
        }

        return;
    }


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    设置联动滚动条触发事件函数; 多个对象时设置其他对象的滚动条触发函数
     * 参数:
     *    NA
     * 返回:
     *    NA
     * 例子:
     *    NA
     * 备注:
     *    NA
     * 修改:
     *    1. 类型 : 创建
     *       作者 : 巫昭雯
     *       时间 : 2021-05-26
     *       内容 : 所有代码
    ************************************************************************************************/
    this.set_link_scroll_fn = function () {
        obj_scroll_wrap.onscroll = function (ev) {
            ev = ev || window.event;
            ev.currentTarget.onscroll = scroll_event;
        };
    };

    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    获取控制滚动条的id
     * 参数:
     *    NA
     * 返回：
     *    @returns { Promise<String> } 控制滚动条的id
     * 例子：
     *    NA
     * 备注：
     *    NA
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2020.4.3
     *      内容 : 所有代码
     ************************************************************************************************/
    this.get_scroll_id = function () {
        return mc_main_wrap_id;
    };


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    设置联动滚动的滚动条位置
     * 参数:
     *    @param { Promise<String> } id 控制滚动条的id
     *    @param { Promise<String> } left 滚动条的left值
     *    @param { Promise<String> } top 滚动条的top值
     * 返回：
     *    NA
     * 例子：
     *    NA
     * 备注：
     *    NA
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2020.4.3
     *      内容 : 所有代码
     ************************************************************************************************/
    this.link_scroll = function (id, left, top) {
        set_scroll_pos(id, left, top);
    };


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    设置滚动条滚动时导航栏的位置以及滚动条的位置
     * 参数:
     *    @param { Promise<String> } scroll_id 控制滚动条的id
     *    @param { Promise<String> } left 滚动条的left值
     *    @param { Promise<String> } top 滚动条的top值
     * 返回：
     *    NA
     * 例子：
     *    NA
     * 备注：
     *    NA
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2020.4.3
     *      内容 : 所有代码
     ************************************************************************************************/
    function set_scroll_pos(scroll_id, left, top) {
        if ("undefined" === typeof scroll_id || "undefined" === typeof left || "undefined" === typeof top) {
            return;
        }

        // 使用定义的id
        // $("#" + mc_main_wrap_id).scrollTop(top).scrollLeft(left);

        // 使用传进来的id
        $("#" + scroll_id).scrollTop(top).scrollLeft(left);

        var set_left = -top;
        var set_top = -left;

        // 设置导航位置
        $("#" + mc_nav_wrap_id).css("top", set_left + "px");
        $("#" + mc_head_wrap_id).css("left", set_top + "px");
    }


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    设置滚动条x轴为最大值
     * 参数:
     *    NA
     * 返回：
     *    NA
     * 例子：
     *    NA
     * 备注：
     *    NA
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2020.4.3
     *      内容 : 所有代码
     ************************************************************************************************/
    this.set_scroll_x_max_val = function () {
        var max_scroll_val = obj_scroll_wrap.scrollWidth;
        var container_width = obj_scroll_wrap.clientWidth;
        var scroll_left = max_scroll_val - container_width;

        set_scroll_pos(mc_main_wrap_id, scroll_left, 0);
    };


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    更新滚动条
     * 参数:
     *    NA
     * 返回：
     *    NA
     * 例子：
     *    NA
     * 备注：
     *    NA
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2020.4.3
     *      内容 : 所有代码
     ************************************************************************************************/
    this.update_scroll = function () {
        set_scroll_pos(mc_main_wrap_id, obj_scroll_wrap.scrollLeft, obj_scroll_wrap.scrollTop);
    };


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    设置键盘事件对应方向建触发相应事件：设置选中块样式
     * 参数:
     *    @param { Promise<String> } top_pos top值
     *    @param { Promise<String> } left_pos left值
     * 返回：
     *    NA
     * 例子：
     *    NA
     * 备注：
     *    NA
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2019.12.17
     *      内容 : 所有代码
     ************************************************************************************************/
    function set_choose_div_class_keydown(top_pos, left_pos) {
        // 键盘事件移动时是否存在已创建的div
        var b_existed = false;
        // 编辑存在的div下标
        var exist_div_idx = 0;
        var arr_create_div = arr_create_obj;
        var arr_create_div_len = arr_create_div.length;

        if (0 !== arr_create_div_len) {
            for (var idx = 0; idx < arr_create_div_len; idx++) {
                var item = arr_create_div[idx];
                var class_list = item.classList;

                class_list.remove("box_selection_creat_div");

                if (item.offsetTop === Number(top_pos) && item.offsetLeft === Number(left_pos)) {
                    b_existed = true;
                    exist_div_idx = idx;
                }
            }
            if (b_existed) {
                var item_exist = arr_create_div[exist_div_idx];

                item_exist.classList.add("box_selection_creat_div");
                arr_box_select_obj = [item_exist];
                set_style_after_drag("键盘事件存在生成块", ui_selected_xy);
                var formula = item_exist.getAttribute("formula");

                set_edit_box_val(formula);
                item_exist.focus();
            } else {
                if (!b_is_virtual_point_mode) {
                    ui_click_num++;
                }

                ui_click_num_virtual++;
                var id = "click_div_" + ui_click_num_virtual;

                create_div_mouse_click(top_pos, left_pos, get_g_max_val() + 1, id);
                set_style_after_drag("键盘事件不存在生成块", ui_selected_xy);
                // mc_svg_tb_hardware_callback(2,get_next_dot_data());
                mc_svg_tb_hardware_callback(2, true);
            }
        }
    }

    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    图层鼠标右击事件：右击取消框选事件
     * 参数:
     *   @param { Promise<Object> } 鼠标事件默认参数
     * 返回：
     *    NA
     * 例子：
     *    NA
     * 备注：
     *    NA
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2019.12.17
     *      内容 : 所有代码
     ************************************************************************************************/

    // mc_svg.onmousedown = function (event) {
    //     event.stopPropagation();
    //     var e = event || window.event;

    //     // 点击鼠标右键 取消框选
    //     if (2 === e.button) {
    //         obj_box_selection_wrap_div.attributes.style.value = "display:none";
    //         remove_selected_div_class();
    //         $("#" + mc_svg_id + " .click_create_div").css("pointer-events","auto");
    //     }
    // };


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    取消掉网页右击默认事件
     * 参数:
     *    NA
     * 返回：
     *    @returns { Promise<Boolean> }
     * 例子：
     *    NA
     * 备注：
     *    NA
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2019.12.17
     *      内容 : 所有代码
     ************************************************************************************************/
    mc_svg.oncontextmenu = function () {
        return false;
    };

    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    设置显示位置值;
     * 参数:
     *    @param { Promise<String>} pos 位置值字符串
     * 返回：
     *    NA
     * 例子：
     *    NA
     * 备注：
     *    NA
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2020.4.1
     *      内容 : 所有代码
     ************************************************************************************************/
    function set_pos_div_val(pos) {
        // var str_val = "(" + x + ":" + y + ")";

        obj_pos_div.innerHTML = pos;
    }


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    设置编辑框值;
     * 参数:
     *    @param { Promise<String>} val 需要设置的值
     * 返回：
     *    NA
     * 例子：
     *    NA
     * 备注：
     *    NA
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2020.4.1
     *      内容 : 所有代码
     ************************************************************************************************/
    function set_edit_box_val(val) {
        // console.trace(val);

        obj_edit_box_div.innerText = val;

        var str = JSON.stringify(val);

        if ("" === str.trim()) {
            remove_edit_pos_div();
        }
    }

    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    获取编辑框值;
     * 参数:
     *    @param { Promise<Object>} obj 需要获取的innerText对象
     * 返回：
     *    @returns { Promise<String> }  处理过的编辑框值
     * 例子：
     *    NA
     * 备注：
     *    已经过处理的编辑框值：
     *      已清空编辑框值内的空格,替换掉中文状态下的"(" ,")",":" 符号；
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2020.4.1
     *      内容 : 所有代码
     ************************************************************************************************/
    function get_obj_innerText_val(obj) {
        if (obj) {
            // 清空空格
            var re_val = obj.innerText.trim().replace(/\s*/g, "");

            return replace_symbol(re_val);
        }
        return false;
    }

    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    替换符号为英文格式;
     * 参数:
     *    @param { Promise<String>} str 需要替换的字符串
     * 返回：
     *    @returns { Promise<String>} 替换后的字符串 || 输入的值
     * 例子：
     *    NA
     * 备注：
     *    符号包括 （ || ）|| ：
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2020.4.1
     *      内容 : 所有代码
     ************************************************************************************************/
    function replace_symbol(str) {
        if ("string" !== typeof str) {
            return str;
        }

        // 匹配符号;（ || ）|| ：
        var RexStr = /（|）|：|&/g;
        var re_str = str.replace(RexStr, function (MatchStr) {
            switch (MatchStr) {
            case "（":
                return "(";
            case "）":
                return ")";
            case "：":
                return ":";
            default:
                break;
            }
            return str;
        });

        return re_str;
    }

    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    生成位置块div;
     * 参数:
     *    @param { Promise<String>} id 生成的块id
     *    @param { Promise<String>} left 生成的块left值
     *    @param { Promise<String>} top 生成的块top值
     * 返回：
     *    NA
     * 例子：
     *    NA
     * 备注：
     *    NA
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2020.4.1
     *      内容 : 所有代码
     ************************************************************************************************/
    function create_pos_div(id, left, top) {
        var pos_div = "<div id='" + id + "' class='edit_select'></div>";

        $obj_svg_jq.append(pos_div);

        $("#" + id).css({
            "width": each_rect_width,
            "height": each_rect_height,
            "top": top,
            "left": left,
            "line-height": each_rect_height
        });

        // document.getElementById(id).style.cssText += "top:" + top + "px;";
    }


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    移除位置块div;
     * 参数:
     *    NA
     * 返回：
     *    NA
     * 例子：
     *    NA
     * 备注：
     *    NA
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2020.4.1
     *      内容 : 所有代码
     ************************************************************************************************/
    function remove_edit_pos_div() {
        var arr_pos = mc_svg.getElementsByClassName("edit_select");
        var arr_len = arr_pos.length;

        if (0 === arr_len) {
            return;
        }

        for (var idx = 0; idx < arr_len; idx++) {
            var item = arr_pos[0];
            // arr_pos[0].remove();

            item.parentElement.removeChild(item);
        }
    }


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    编辑框焦点事件
     * 参数:
     *    @param { Promise<Object> } event 事件event
     * 返回：
     *    NA
     * 例子：
     *    NA
     * 备注：
     *    NA
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2020.4.2
     *      内容 : 所有代码
     ************************************************************************************************/
    function edit_box_div_blur(event) {
        // console.trace("编辑框blur函数" , e);

        var type = event.type;
        var obj_cur = event.target;

        if ("focus" === type) {
            // 只能设置单个
            if (1 !== arr_box_select_obj.length) {
                this.blur();
                return;
            }

            // 绑定联动对象
            this.link_create_obj = arr_box_select_obj[0];
            obj_cur.contentEditable = true;
            obj_cur.setAttribute("is_edit", true);
            var obj_sel = arr_box_select_obj[0];

            // 保存联动之前的数据 pre_edit === 为编辑之前div显示的值
            obj_sel.pre_edit = obj_sel.innerText;
            // 获的焦点时联动框选对象
            edit_box_link_set_selectd_div_val(obj_cur.innerText);
            return;
        }

        if ("blur" === type) {
            obj_cur.contentEditable = false;
            obj_cur.setAttribute("is_edit", false);
            set_create_div_val(this.link_create_obj);
        }

        event.stopPropagation();
        event.preventDefault();
        return;
    }

    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    编辑框键盘按下keydown事件;
     * 参数:
     *    @param { Promise<Object>} event 键盘事件参数event
     * 返回：
     *    NA
     * 例子：
     *    NA
     * 备注：
     *    NA
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2020.4.1
     *      内容 : 所有代码
     ************************************************************************************************/
    function edit_box_div_keydown(event) {
        var key_code = event.keyCode;
        var key_val = event.key;

        // console.log(event);

        var b_is_arrow_keys = 37 === key_code || 38 === key_code || 39 === key_code || 40 === key_code;

        if (!b_is_arrow_keys) {
            mc_svg_tb_hardware_callback(0);
        }


        // 回车
        if ("keydown" === event.type && 13 === event.keyCode) {
            // 阻止换行
            event.preventDefault();
            event.stopPropagation();
            obj_edit_box_div.blur();
            return;
        }

        // 清空is_edit
        if ("Delete" === key_val) {
            set_edit_box_val("");
            // edit_box_link_set_selectd_div_val();
            return;
        }

        // 输入等号
        if ("Equal" === event.code && false === event.shiftKey) {
            set_edit_box_val("");
            obj_edit_box_div.setAttribute("is_edit", true);
            return;
        }


        // 清空位置值
        var val = get_obj_innerText_val(obj_edit_box_div);

        // 联动数据
        edit_box_link_set_selectd_div_val(val);
        real_time_chg_pos_div(val);
    }


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    编辑框键盘释放keyup事件; 输入时实时显示位置值；
     * 参数:
     *    @param { Promise<String>} event 键盘事件参数event
     * 返回：
     *    NA
     * 例子：
     *    NA
     * 备注：
     *    目的：输入时实时显示位置值；
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2020.4.1
     *      内容 : 所有代码
     ************************************************************************************************/
    function edit_box_div_keyup(event) {
        // console.log("编辑框键盘事件keyup");
        event.preventDefault();

        // 只能设置单个
        if (1 !== arr_box_select_obj.length) {
            this.blur();
            return;
        }

        // 绑定联动对象
        this.link_create_obj = arr_box_select_obj[0];
        var val = get_obj_innerText_val(obj_edit_box_div);

        edit_box_link_set_selectd_div_val(val);

        if (13 !== event.keyCode) {
            real_time_chg_pos_div(val);
        }
    }


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    编辑框编辑联动设置框选div的值
     * 参数:
     *    @param { Promise<String>} val 联动值
     * 返回：
     *    NA
     * 例子：
     *    NA
     * 备注：
     *    目的：输入时实时显示位置值；
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2020.4.1
     *      内容 : 所有代码
     ************************************************************************************************/
    function edit_box_link_set_selectd_div_val(val) {
        // console.log(val);
        // 框选对象
        var obj = arr_box_select_obj[0];

        obj.classList.add("click_create_div_edit");

        if ("undefined" !== val) {
            obj.innerText = val;
        }
    }


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    拖拽复制创建虚拟div块
     * 参数:
     *    @param { Promise<Object>} data 创建的数据对象
     * 返回：
     *    NA
     * 例子：
     *    NA
     * 备注：
     *    NA
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2020.3.14
     *      内容 : 所有代码
     ************************************************************************************************/
    function create_virtual_div_copy(data) {
        // var is_edit = false;
        var x_val = data.left / each_rect_width + 1;
        var y_val = data.top / each_rect_height + 1;
        var pos_val = "(" + x_val + ":" + y_val + ")";
        var id_real = id_name + "_" + data.id + "_" + x_val + "_" + y_val + "_" + (Math.random() * 1000).toFixed();
        var sweep = data.sweep;
        var point = data.point;

        // 定义堆栈数据
        var param_data = {
            pos: pos_val,
            init_fml: data.init_fml,
            formula: data.formula,
            point: point,
            sweep: sweep
        };

        // 判断当前是需要生成新的还是修改已存在的
        var obj_cur = get_cur_pos_create_obj(data.left, data.top);

        if (obj_cur) {
            var old_formula = obj_cur.getAttribute("formula");

            // 当存在的生成点的公式值改变了或虚点值改变了才入栈
            if (compare_formula_values(old_formula, data.formula) || Number(obj_cur.getAttribute("point")) !== Number(data.point)) {
                param_data.b_is_init = false;
                param_data.id = obj_cur.id;
                // 入栈
                set_val_change_action(param_data);
            }

            obj_cur.setAttribute("formula", data.formula);
            obj_cur.setAttribute("init_fml", data.init_fml);
            obj_cur.setAttribute("sweep", sweep);
            obj_cur.setAttribute("point", point);
            obj_pos_formula[pos_val] = data.formula;
            return;
        }

        var str_click_div = "<div id='" + id_real + "' div_type='" + get_div_type_val(0) + "' tabindex='0' contenteditable='false' class='click_create_div' style='top:" + data.top + "px;left:" + data.left + "px;width:" + each_rect_width + "px;height:" + each_rect_height +
            "px;line-height:" + each_rect_height + "px;' x='" + x_val + "' y='" + y_val + "'pos='" + pos_val + "' init_fml='" + data.init_fml + "' formula='" + data.formula +
            "' sweep='" + sweep + "' point='" + point + "'>" + data.val + "</div>";

        $obj_svg_jq.append(str_click_div);

        // var obj_div_click = document.getElementById(id_real);

        operation_of_new_create_point(document.getElementById(id_real));

        // 更新位置对象
        obj_pos_formula[pos_val] = data.formula;
        ui_click_num++;

        param_data.b_is_init = true;
        param_data.id = id_real;

        // 入栈
        set_val_change_action(param_data);
    }

    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *   获取当前位置的是否有生成对象
     * 参数:
     *    @param { Promise<String>} left 当前位置的left值
     *    @param { Promise<String>} top 当前位置的top值
     * 返回：
     *    @returns { Promise<Object>} 返回生成的对象 || false
     * 例子：
     *    NA
     * 备注：
     *    NA
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2020.4.16
     *      内容 : 所有代码
     ************************************************************************************************/
    function get_cur_pos_create_obj(left, top) {
        var len = arr_exist_obj_copy.length;

        for (var index = 0; index < len; index++) {
            var item = arr_exist_obj_copy[index];

            if (item.offsetLeft === left && item.offsetTop === top) {
                return item;
            }
        }
        return false;
    }


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *   分解公式获取公式的位置值
     * 参数:
     *    @param { Promise<String>} str 公式字符串
     * 返回：
     *    @returns { Promise<Array> } 公式值字符串数组
     * 例子：
     *    NA
     * 备注：
     *    NA
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2020.3.14
     *      内容 : 所有代码
     ************************************************************************************************/
    function break_down_formula(str) {
        if ("string" !== typeof str) {
            return false;
        }

        // 存放左边括号
        var leftArr = [];
        // 存放成功数据
        var successArr = [];

        /*遍历字符串*/
        for (var idx = 0; idx < str.length; idx++) {
            if ("(" === str[idx] || "（" === str[idx]) {
                leftArr.push(idx);
            } else if (")" === str[idx] || "）" === str[idx]) {
                if (0 === leftArr.length) {
                    show_info_massage("MC_LANG_POPOUT_CHARATER_ERROR");
                    return str;
                }
                var start = leftArr.pop();
                var pos = str.substring(start, idx + 1);

                if (-1 !== pos.indexOf(":") || -1 !== pos.indexOf("：")) {
                    if (1 !== pos.match(/:|：/g).length) {
                        return false;
                    }

                    if (false === test_pos_is_vaild(pos)) {
                        return false;
                    }

                    successArr.push({
                        pos: pos,
                        start: start,
                        end: idx + 1,
                        val: get_pos_val(pos).val,
                        formula: get_pos_val(pos).formula
                    });
                }
            }
        }
        // 检查leftArr的长度是否为0
        if (0 !== leftArr.length) {
            show_info_massage("MC_LANG_POPOUT_CHARATER_ERROR");
            return str;
        }

        if (0 !== successArr.length) {
            return successArr;
        }
        return str;
    }


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    获取位置值对应的val;
     * 参数:
     *    @param { Promise<String>} formula 公式字符串
     * 返回：
     *    @returns { Promise<Object> } 含有新的工时制 和 val 对象
     * 例子：
     *    NA
     * 备注：
     *    NA
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2020.3.14
     *      内容 : 所有代码
     ************************************************************************************************/
    function get_pos_val(formula) {
        // var arr_create_div = document.getElementsByClassName("click_create_div");
        var arr_create_div = arr_create_obj;
        var str_breker_left = formula.substring(1, formula.length - 1);

        var formula_x = str_breker_left.split(":")[0];
        var formula_y = str_breker_left.split(":")[1];

        if (1 > Number(formula_x) || col < Number(formula_x) || 1 > Number(formula_y) || row < Number(formula_y)) {
            // throw new Error("位置值错误");
            show_info_massage("MC_LANG_POPOUT_WRONG_OSITION_VALUE");
            return false;
        }


        for (var idx = 0; idx < arr_create_div.length; idx++) {
            var item = arr_create_div[idx];
            var item_pos = item.getAttribute("pos");
            // var item_formula = item.getAttribute("formula");
            // var item_x = item.getAttribute("x");
            // var item_y = item.getAttribute("y");

            if (formula === item_pos) {
                return {
                    val: item.innerText,
                    formula: item.item_formula
                };
            }
        }

        return {
            val: 0,
            formula: ""
        };
    }


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    将位置值替换为内容值;
     * 参数:
     *    @param { Promise<String>} str 位置值字符串(不包含等号)
     *    @param { Promise<Array>} arr 包含的位置数据；
     * 返回：
     *    @returns { Promise<Object>} 返回对象包含可计算的值；和新的位置值
     * 例子：
     *    NA
     * 备注：
     *    NA
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2020.3.14
     *      内容 : 所有代码
     ************************************************************************************************/
    function replace_position(str, arr) {
        var new_str = "";
        var new_str_start = "";
        var new_str_end = "";

        var new_str_pos = "";
        var new_str_pos_start = "";
        var new_str_pos_end = "";

        if (0 !== arr[0].start) {
            new_str_start = str.substring(0, arr[0].start);
            new_str_pos_start = str.substring(0, arr[0].start);
        }

        if (arr[arr.length - 1].end !== str.length) {
            new_str_end = str.substring(arr[arr.length - 1].end, str.length);
            new_str_pos_end = str.substring(arr[arr.length - 1].end, str.length);
        }

        for (var idx = 0; idx < arr.length; idx++) {
            var item = arr[idx];
            var next = arr[idx + 1];
            var str_rest = "";

            if (next) {
                str_rest = str.substring(item.end, next.start);
            }
            new_str = new_str + item.val + str_rest;

            if ("undefined" !== typeof item.new_pos) {
                new_str_pos = new_str_pos + item.new_pos + str_rest;
            }
        }

        // 返回新的位置和可计算的数学表达式
        return {
            pos: new_str_pos_start + new_str_pos + new_str_pos_end,
            calc_val: new_str_start + new_str + new_str_end
        };
    }


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    生成块键盘按下事件(选中的生成块)
     * 参数:
     *    @param { Promise<Object>} event 键盘事件参数event
     *    @param { Promise<boolean>}  b_wrap 是否为外层区域
     * 返回：
     *    NA
     * 例子：
     *    NA
     * 备注：
     *    NA
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2020.5.18
     *      内容 : 所有代码
     ************************************************************************************************/
    function create_div_keydown(event, b_wrap) {
        // 定义失焦事件
        this.onblur = create_div_blur;
        // event.stopPropagation();

        // 若取消选中则return;
        if (0 === arr_box_select_obj.length) {
            event.stopPropagation();
            return;
        }

        var key_code = event.keyCode;
        // var key_val = event.key;
        var obj_cur = event.target;

        // 框选对象
        var obj_sel = arr_box_select_obj[0];

        if (b_wrap && obj_cur !== obj_sel) {
            // 防止外层选中没有触发键盘事件
            obj_cur = obj_sel;
        }

        // 方向键
        var arr_dir_key = [37, 38, 39, 40];
        // 定义键盘功能键：enter || "shift" || ctrl || tab || capslock
        var arr_function_keys = [13, 16, 17, 20, 9];
        // 可编辑属性
        var b_is_edit = obj_cur.isContentEditable;


        // 编辑状态下 ctrl 时阻止事件分发
        if (event.ctrlKey && b_is_edit) {
            event.stopPropagation();
            return;
        }

        // 功能键 || window 键
        if (event.shiftKey || event.altKey || event.ctrlKey || event.metaKey) {
            return;
        }

        if (1 < arr_box_select_obj.length) {
            event.stopPropagation();
            show_info_massage("MC_LANG_POPOUT_ONLY_ONE");
            return;
        }

        // 虚点模式
        if (obj_sel.getAttribute("div_type") === get_div_type_val(3)) {
            event.preventDefault();
            event.stopPropagation();
            create_div_keyboard_event_dir();
            return;
        }

        // 选中的时候直接输入；获取光标并且清空值
        if (-1 === arr_dir_key.indexOf(key_code) && false === b_is_edit && -1 === arr_function_keys.indexOf(key_code)) {
            if (115 !== key_code) {
                create_block_keydown_first(obj_cur, event.key);
            }
            // return 之后已阻止后置设置onkeyup事件触发
            return;
        }

        // 回车;不做任何操作
        if (13 === key_code) {
            // 阻止换行
            event.preventDefault();
            if (false === b_is_edit) {
                return;
            }

            // obj_cur.classList.remove("click_create_div_edit");
            obj_cur.contentEditable = false;
            obj_edit_box_div.setAttribute("is_edit", false);
            set_create_div_val(obj_cur);
            return;
        }


        // 方向键触发事件; 条件:当前不为可编辑状态时
        if (-1 !== arr_dir_key.indexOf(key_code) && false === b_is_edit) {
            event.preventDefault();
            event.stopPropagation();
            // if (prompt_page_zoom()) {
            //     return;
            // }
            create_div_keyboard_event_dir();
            return;
        }


        // 联动数据
        set_edit_box_val(obj_cur.innerText);
        real_time_chg_pos_div(obj_cur.innerText);


        this.onkeyup = create_div_keydown_to_up;
        /************************************************************************************************
         * 类型:
         *    函数
         * 功能:
         *    生成块键盘释放事件(选中的生成块)；联动编辑框数据
         * 参数:
         *    @param { Promise<Object>} e_keyup 键盘事件参数event
         * 返回：
         *    NA
         * 例子：
         *    NA
         * 备注：
         *    联动编辑框数据
         * 修改:
         *   1. 类型 : 创建
         *      作者 : 巫昭雯
         *      时间 : 2020.5.18
         *      内容 : 所有代码
         ************************************************************************************************/
        function create_div_keydown_to_up(e_keyup) {
            var code_keyup = e_keyup.keyCode;
            // var key_keyup = e_keyup.key;

            var obj_up = e_keyup.currentTarget;
            var set_val = obj_cur.innerText;

            if (0 === arr_box_select_obj.length) {
                event.stopPropagation();
                return;
            }


            // 回车；阻止换行
            if ("keyup" === e_keyup.type && 13 === code_keyup) {
                // event.preventDefault();
                return;
            }

            // 方向键
            if (-1 !== arr_dir_key.indexOf(code_keyup)) {
                return;
            }

            // 清空；Delete键
            if (46 === e_keyup.which) {
                set_val = "";
                obj_up.innerText = set_val;
                set_edit_box_val(set_val);
                return;
            }

            // 输入等号
            if ("Equal" === e_keyup.code && false === e_keyup.shiftKey) {
                set_edit_box_val(set_val);
                obj_edit_box_div.setAttribute("is_edit", true);
                return;
            }

            // 联动设置编辑框值
            set_edit_box_val(set_val);

            if (13 !== e_keyup.keyCode) {
                real_time_chg_pos_div(set_val);
            }
        }

        event.stopPropagation();
        return;
    }


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    生成块键盘事件第一次按下(选中的生成块)
     * 参数:
     *    @param { Promise<Object>} obj_cur 键盘事件参数event
     *    @param { Promise<String>}  key_val 键盘事件event.key值
     * 返回：
     *    NA
     * 例子：
     *    NA
     * 备注：
     *    第一次按下清空选中块数据值
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2020.5.18
     *      内容 : 所有代码
     ************************************************************************************************/
    function create_block_keydown_first(obj_cur, key_val) {
        // console.log("获取光标");
        obj_cur.classList.add("click_create_div_edit");
        obj_cur.contentEditable = true;
        obj_cur.innerText = "";

        if ("=" === key_val) {
            obj_edit_box_div.setAttribute("is_edit", true);
        }

        if ("Delete" === key_val || "Backspace" === key_val) {
            set_edit_box_val("");
        } else {
            set_edit_box_val(key_val);
        }

        set_cursor_position_to_end(obj_cur);
    }


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    实时变动位置div
     * 参数:
     *    @param { Promise<String>} edit_val 编辑值
     * 返回：
     *    NA
     * 例子：
     *    NA
     * 备注：
     *    NA
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2020.4.2
     *      内容 : 所有代码
     ************************************************************************************************/
    function real_time_chg_pos_div(edit_val) {
        var str = edit_val;

        if ("undefined" === typeof str) {
            return;
        }

        remove_edit_pos_div();

        // 清空字符串并转换字符
        str = replace_symbol(str.replace(/\s*/g, ""));

        // 位置值格式正则
        var reg_pos = /(\([0-9]+:[0-9]+\))/g;
        var arr_exec_result = [];

        if (!reg_pos.test(str)) {
            return;
        }

        // 清空下标.使reg.exec(str)从第一个匹配开始
        reg_pos.lastIndex = 0;

        while (null !== (arr_exec_result = reg_pos.exec(str))) {
            var pos = arr_exec_result[1];
            var x_y = pos.match(cui_reg_int);
            var x = Number(x_y[0]);
            var y = Number(x_y[1]);

            // 无效位置
            if (1 > x || col < x || 1 > y || row < y) {
                continue;
            }

            var left = (x - 1) * each_rect_width;
            var top = (y - 1) * each_rect_height;
            var id = "edit_" + x + "_" + y;

            create_pos_div(id, left, top);
        }
    }


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    生成块失焦事件
     * 参数:
     *    @param { Promise<Object>} e_blur 鼠标事件参数event
     * 返回：
     *    NA
     * 例子：
     *    NA
     * 备注：
     *    NA
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2020.5.18
     *      内容 : 所有代码
     ************************************************************************************************/
    function create_div_blur(e_blur) {
        var obj_blur = e_blur.target;
        var b_is_edit = obj_blur.isContentEditable;

        obj_blur.classList.remove("click_create_div_edit");
        if (false === b_is_edit) {
            return;
        }

        // 若当前块为编辑状态才执行设置值事件
        set_create_div_val(obj_blur);
        // 失焦之后设置可编辑属性为false
        obj_blur.contentEditable = false;
        // 注释掉失焦状态
        obj_blur.onblur = null;
    }


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *   生成块 双击事件
     * 参数:
     *    @param { Promise<Object>} event 鼠标事件参数event
     * 返回：
     *    NA
     * 例子：
     *    NA
     * 备注：
     *    生成块 双击进入编辑状态
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2020.5.18
     *      内容 : 所有代码
     ************************************************************************************************/
    function create_div_dblclick(event) {
        // console.log("dblclick", event);
        if (b_sel_status) {
            // return;
        }

        var arr_pos = mc_svg.getElementsByClassName("edit_select");
        var arr_len = arr_pos.length;

        if (0 !== arr_len) {
            return;
        }

        var obj_cur = event.target;

        // 添加编辑样式
        obj_cur.classList.add("click_create_div_edit");
        obj_cur.focus();
        $("#" + mc_svg_id + " .click_create_div").attr("contenteditable", "false");
        obj_cur.contentEditable = true;
        // 双击之后显示值为公式
        // obj_cur.innerText = obj_cur.getAttribute("formula");
        obj_cur.innerText = obj_edit_box_div.innerHTML;
        set_cursor_position_to_end(obj_cur);
        // 定义失焦事件
        obj_cur.onblur = create_div_blur;
    }


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    设置光标在获得焦点的块值之后(设置光标到最后)
     * 参数:
     *    @param { Promise<Object>} obj 设置光标位置的对象节点
     * 返回：
     *    NA
     * 例子：
     *    NA
     * 备注：
     *    NA
     * 例子：
     *    NA
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2020.4.3
     *      内容 : 所有代码
     ************************************************************************************************/
    function set_cursor_position_to_end(obj) {
        if (obj) {
            if (window.getSelection) {
                //解决ff不获取焦点无法定位问题
                // obj.focus();
                var obj_selection = window.getSelection();
                // var focus_obj = document.activeElement;
                // obj_selection.removeAllRanges();

                // obj_selection 选择obj下所有子内容
                obj_selection.selectAllChildren(obj);
                //光标移至最后
                obj_selection.collapseToEnd();
            } else if (document.selection) {
                //ie10 9 8 7 6 5
                var range = document.selection.createRange();
                //var range = document.body.createTextRange();

                //range定位到obj
                range.moveToElementText(obj);
                //光标移至最后
                range.collapse(false);
                range.select();
            }
        }
    }


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *     获取当前光标对象并添加鼠标选中的位置值
     * 参数:
     *    @param { Promise<String>} x 鼠标选中的位置x值
     *    @param { Promise<String>} y 鼠标选中的位置y值
     * 返回：
     *    NA
     * 例子：
     *    NA
     * 备注：
     *    NA
     * 例子：
     *    NA
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2020.5.18
     *      内容 : 所有代码
     ************************************************************************************************/
    function add_cursor_pos_val(x, y) {
        var pos_val = "(" + x + ":" + y + ")";
        var obj_sel = window.getSelection();
        // var selRange = obj_sel.getRangeAt(0);
        var anchor_Offset = obj_sel.anchorOffset;
        var focus_Offset = obj_sel.focusOffset;

        var obj_re = obj_sel.focusNode;
        var text = obj_re.textContent;

        // 选中区域起始偏移量
        var offset_start = 0;
        var offset_end = 0;

        // 光标位置
        if (anchor_Offset > focus_Offset) {
            offset_start = focus_Offset;
            offset_end = anchor_Offset;
        } else {
            offset_start = anchor_Offset;
            offset_end = focus_Offset;
        }

        var obj_div_node = obj_sel.focusNode;

        if ("#text" === obj_re.nodeName) {
            obj_div_node = obj_sel.focusNode.parentNode;
        }

        var str_div_type = obj_div_node.getAttribute("div_type");

        // 设置联动数据；当前光标对象不为编辑框的时候需要更新编辑框的值
        switch (str_div_type) {
        case get_div_type_val(1):
            obj_re.textContent = insert_str_in_index(text, offset_start, offset_end, pos_val);
            obj_sel.collapse(obj_sel.focusNode, offset_start + pos_val.length);
            edit_box_link_set_selectd_div_val(obj_re.textContent);
            break;
        case get_div_type_val(0):
            obj_re.textContent = insert_str_in_index(text, offset_start, offset_end, pos_val);
            obj_sel.collapse(obj_sel.focusNode, offset_start + pos_val.length);
            set_edit_box_val(obj_re.textContent);
            break;
        default:
            break;
        }
    }


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *     在指定位置插入字串
     * 参数:
     *    @param { Promise<String>} str 原字符串
     *    @param { Promise<Number>} idx_start 指定位置开始值
     *    @param { Promise<Number>} idx_end 指定位置结束值
     *    @param { Promise<String>} insert_str 需要插入的字符串
     * 返回：
     *    @returns { Promise<String>} 新的字符串 || false
     * 例子：
     *    NA
     * 备注：
     *    NA
     * 例子：
     *    NA
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2020.5.18
     *      内容 : 所有代码
     ************************************************************************************************/
    function insert_str_in_index(str, idx_start, idx_end, insert_str) {
        if ("string" !== typeof str || "string" !== typeof insert_str || "number" !== typeof idx_start || "number" !== typeof idx_end) {
            return false;
        }
        return str.slice(0, idx_start) + insert_str + str.slice(idx_end, str.length);
    }


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *     设置生成块的编辑之后的值；回车设置值
     * 参数:
     *    @param { Promise<Object>} obj 生成对象
     * 返回：
     *    NA
     * 例子：
     *    NA
     * 备注：
     *    可以输入直接显示行扫数的值；例子："1_2"
     * 例子：
     *    NA
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2020.5.18
     *      内容 : 所有代码
     ************************************************************************************************/
    function set_create_div_val(obj) {
        var arr_selected_div = arr_box_select_obj;
        var arr_len = arr_selected_div.length;

        if (0 === arr_len) {
            show_info_massage("MC_LANG_POPOUT_UNSELECTED");
            return;
        }

        if (1 !== arr_len) {
            show_info_massage("MC_LANG_POPOUT_ONLY_ONE");
            return;
        }

        // 需要改变的值的对象
        var obj_val_chg = arr_selected_div[0];

        if (arr_selected_div[0] !== obj && obj) {
            obj_val_chg = obj;
        } else {
            // 编辑框失焦之后给框选对象获得焦点；防止编辑框失焦之后直接键盘输入出现错误
            obj_val_chg.focus();
        }

        // 移除编辑样式
        obj_val_chg.classList.remove("click_create_div_edit");

        // 编辑框的值;
        var old_val = obj_val_chg.pre_edit;
        var edit_val = get_obj_innerText_val(obj_val_chg);
        var pre_fomular = obj_val_chg.getAttribute("formula");

        // 若公式不变则return
        if (edit_val === pre_fomular) {
            obj_val_chg.innerText = old_val;
            return;
        }

        var edit_formula = edit_val;
        var cur_pos = obj_val_chg.getAttribute("pos");
        var calc_val = "";

        try {
            calc_val = find_child_formula(cur_pos, edit_val);
            if (false === calc_val || 0 >= calc_val) {
                // 最后计算值错误 || 数值小于 0
                throw Error("MC_LANG_INVALID");
            }
        } catch (error) {
            remove_edit_pos_div();
            obj_val_chg.innerText = old_val;
            set_edit_box_val(pre_fomular);
            show_info_massage(error.message);
            return;
        }

        // 可以输入"1_2"格式的值
        if ("string" === typeof calc_val) {
            if (-1 !== calc_val.indexOf(cui_underline)) {
                var arr_int = calc_val.match(cui_reg_int);

                if (2 !== arr_int.length) {
                    obj_val_chg.innerText = old_val;
                    set_edit_box_val(pre_fomular);
                    show_info_massage("MC_LANG_INVALID");
                    return;
                }

                var val_sweep = Number(arr_int[0]) - 1;
                var val_point = Number(arr_int[1]);

                calc_val = val_point + val_sweep * obj_external_mod_data.ui_single_sweep_len;
                edit_formula = calc_val;

                if (0 >= calc_val) {
                    obj_val_chg.innerText = old_val;
                    set_edit_box_val(pre_fomular);
                    show_info_massage("MC_LANG_INVALID");
                    return;
                }
            }
        }

        obj_val_chg.on_val_chg(obj_val_chg.id, old_val, calc_val, edit_formula);
        update_formula_map_val();
        remove_edit_pos_div();
        set_edit_box_val(edit_formula);
    }


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    生成块方向键键盘事件
     * 参数:
     *    @param { Promise<Object> } event 键盘事件默认参数
     * 返回：
     *    NA
     * 例子：
     *    NA
     * 备注：
     *    控件方向键：
     *      左：37 === keyCode
     *      上：28 === keyCode
     *      右：39 === keyCode
     *      下：40 === keyCode
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2019.12.17
     *      内容 : 所有代码
     ************************************************************************************************/
    function create_div_keyboard_event_dir(event) {
        var e = event || window.event;
        var key_code = e.keyCode;
        // 框选对象
        var arr_selected_div = arr_box_select_obj;

        // 判断有选中div并且只选中一个
        if (1 === arr_selected_div.length) {
            var selected_div = arr_selected_div[0];
            var top_pos = selected_div.offsetTop;
            var left_pos = selected_div.offsetLeft;

            var ui_x_num = Number(selected_div.getAttribute("x"));
            var ui_y_num = Number(selected_div.getAttribute("y"));

            // 选中的块不超过边界值
            var b_is_svg_box = 0 !== selected_div.offsetTop || 0 !== selected_div.offsetLeft || selected_div.offsetLeft !== rect_x_width || rect_y_height !== selected_div.offsetTop;

            if (b_is_svg_box && e) {
                // 方向==左
                if (37 === key_code && 0 !== selected_div.offsetLeft && 1 < Math.floor(ui_x_num)) {
                    left_pos = selected_div.offsetLeft - each_rect_width;
                    set_choose_div_class_keydown(top_pos, left_pos);
                    return;
                }
                // 方向==上
                if (38 === key_code && 0 !== selected_div.offsetTop && 1 < Math.floor(ui_y_num)) {
                    top_pos = selected_div.offsetTop - each_rect_height;
                    set_choose_div_class_keydown(top_pos, left_pos);
                    return;
                }
                // 方向==右
                if (39 === key_code && rect_x_width !== selected_div.offsetLeft && col > ui_x_num) {
                    left_pos = Number(selected_div.offsetLeft) + Number(each_rect_width);
                    set_choose_div_class_keydown(top_pos, left_pos);
                    return;
                }
                // 方向==下
                if (40 === key_code && rect_y_height !== selected_div.offsetTop && row > ui_y_num) {
                    top_pos = Number(selected_div.offsetTop) + Number(each_rect_height);
                    set_choose_div_class_keydown(top_pos, left_pos);
                    return;
                }
            }
        }
    }


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    移除拖拽复制框选内已生成的div块
     * 参数:
     *    @param { Promise<Object>} box_select_data 拖拽框选数据对象
     *    @param { Promise<Object>} virtual_div_data 拖拽虚拟div框数据对象
     * 返回：
     *    NA
     * 例子：
     *    NA
     * 备注：
     *    NA
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2020.3.14
     *      内容 : 所有代码
     ************************************************************************************************/
    function remove_box_select_div(box_select_data, virtual_div_data) {
        arr_exist_obj_copy = [];

        var arr_create_div = arr_create_obj;
        var arr_create_div_len = arr_create_div.length;

        // 数据方向dir取值x,y轴的正向或反向：true === 正向 || false === 反向
        var dir = false;

        // 右下
        if (box_select_data.top === virtual_div_data.top && box_select_data.left === virtual_div_data.left) {
            dir = true;
        }

        // 需要复制的外框数据
        var copy_data_left = virtual_div_data.left;
        var copy_data_top = virtual_div_data.top;
        var copy_data_wifth = virtual_div_data.width;
        var copy_data_height = virtual_div_data.height;


        if (virtual_div_data.dir) {
            // 向下
            copy_data_height = virtual_div_data.height - box_select_data.height;
            if (dir) {
                copy_data_top = virtual_div_data.top + box_select_data.height;
            }
        } else {
            // 向右
            copy_data_wifth = virtual_div_data.width - box_select_data.width;
            if (dir) {
                copy_data_left = virtual_div_data.left + box_select_data.width;
            }
        }

        // 测试
        // $(".test_div").css({
        //     width: copy_data_wifth,
        //     height: copy_data_height,
        //     top: copy_data_top,
        //     left: copy_data_left
        // });

        // 最大位置需要剪掉一小段距离位置；为了区分top,left值等于最大位置的div;
        var copy_data_left_max = copy_data_left + copy_data_wifth - 1;
        var copy_data_top_max = copy_data_top + copy_data_height - 1;


        if (0 !== arr_create_div_len) {
            for (var idx = 0; idx < arr_create_div_len; idx++) {
                var item = arr_create_div[idx];

                if ("undefined" !== typeof item) {
                    var item_left = item.offsetLeft;
                    var item_top = item.offsetTop;

                    // 在框选范围之内的div需要移除
                    if (Number(copy_data_left) <= Number(item_left) && Number(item_left) <= Number(copy_data_left_max) &&
                        Number(copy_data_top) <= Number(item_top) && Number(item_top) <= Number(copy_data_top_max)) {
                        item.classList.add("remove_div");
                        arr_exist_obj_copy.push(item);
                    }
                }
            }
        }

        return;

        // $(".remove_div").remove();
    }


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    拖拽复制框选内的单个生成点
     * 参数:
     *    @param { Promise<Object>} obj_box_select_data 拖拽框选数据对象
     *    @param { Promise<Object>} obj_virtual_data 拖拽虚拟框数据对象
     *    @param { Promise<Object>} obj_item_pos_data 框选集合中单项item位置数据对象
     *    @param { Promise<Object>} loop_data 拖拽之后需要循环生成的div块数据对象
     * 返回：
     *    NA
     * 例子：
     *    NA
     * 备注：
     *    obj_div_data 数据为
     *    {
     *     loop_num: loop_data.num,
     *     formula: loop_data.formula,
     *     val: loop_data.val,
     *     top: div_top,
     *     left: div_left
     *    };
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2020.3.14
     *      内容 : 所有代码
     ************************************************************************************************/
    function drag_copy_singel_block(obj_box_select_data, obj_virtual_data, obj_item_pos_data, loop_data) {
        var drag_dir = obj_virtual_data.dir;
        var box_select_dir = obj_box_select_data.dir;

        var div_top = obj_item_pos_data.top;
        var div_left = obj_item_pos_data.left + each_rect_width;

        var obj_step_data = {
            width: each_rect_width,
            height: each_rect_height
        };

        var pos_dir = true;

        // 是否是同一个方向
        if (box_select_dir === drag_dir) {
            obj_step_data.width = obj_box_select_data.width;
            obj_step_data.height = obj_box_select_data.height;

            div_top = obj_item_pos_data.top + obj_box_select_data.height;
            div_left = obj_item_pos_data.left;

            if (false === drag_dir) {
                div_top = obj_item_pos_data.top;
                div_left = obj_item_pos_data.left + obj_box_select_data.width;
            }

            // 框选为左;
            if (obj_box_select_data.left !== obj_virtual_data.left && false === drag_dir) {
                div_left = obj_item_pos_data.left - obj_box_select_data.width;
                pos_dir = false;
            }

            // 框选为上;
            if (obj_box_select_data.top !== obj_virtual_data.top && drag_dir) {
                div_top = obj_item_pos_data.top - obj_box_select_data.height;
                pos_dir = false;
            }
        } else {
            if (drag_dir) {
                div_top = obj_item_pos_data.top + each_rect_height;
                div_left = obj_item_pos_data.left;
            }

            // 框选为上
            if (obj_item_pos_data.top !== obj_virtual_data.top && true === drag_dir) {
                div_top = obj_item_pos_data.top - each_rect_height;
                pos_dir = false;
            }

            // 框选为左
            if (obj_item_pos_data.left !== obj_virtual_data.left && false === drag_dir) {
                div_left = obj_item_pos_data.left - each_rect_width;
                pos_dir = false;
            }
        }


        // 复制给参数
        var obj_div_data = loop_data;

        // 再添加上位置值
        obj_div_data.top = div_top;
        obj_div_data.left = div_left;

        create_copy_div_gp_merge(obj_div_data, obj_step_data, drag_dir, pos_dir);
    }


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    生成一组拖拽复制div; 合并
     * 参数:
     *    @param { Promise<Object>} obj_div_data 生成的div块数据对象
     *    @param { Promise<Object>} obj_step_data 循环步进值对象
     *    @param { Promise<Object>} drag_dir 拖拽方向
     *    @param { Promise<Object>} pos_dir 位置方向
     * 返回：
     *    NA
     * 例子：
     *    NA
     * 备注：
     *    复制计算规则如下：
     *      公式包含位置值：复制的块位置值相应变动计算；
     *          例子：当前选中块的公式为 "=(10:5)+9" 。拖拽复制的公式值递增：
     *          若拖拽方向为下 "=(10:6)+9"
     *          若拖拽方向为上 "=(10:4)+9"
     *          若拖拽方向为左 "=(9:5)+9"
     *          若拖拽方向为右 "=(11:5)+9"
     *      公式不包含位置值：复制的块按递增计算；
     *          例子1: 当前选中块的公式为 "=9" 。拖拽复制的公式值递增： "=10"
     *          例子2: 当前选中块的公式为 "9" 。拖拽复制的公式值递增： "10"
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2020.3.14
     *      内容 : 所有代码
     ************************************************************************************************/
    function create_copy_div_gp_merge(obj_div_data, obj_step_data, drag_dir, pos_dir) {
        // 定义设置的值(可修改); 传递进来的值拿来判断
        var param_init_fml = obj_div_data.init_fml;
        var param_foamula = obj_div_data.formula;
        var param_val = "";

        // 初始公式值和公式值是否相等
        var b_infml_and_fml_is_equal = false;

        // 若初始公式不等于公式值,则表示修改过扫数值;该模式下复制的数据为根据原始公式复制
        if (obj_div_data.init_fml !== obj_div_data.formula) {
            b_infml_and_fml_is_equal = true;
        }

        // 初始公式值是否有等于符号
        var b_init_fml_has_equal_sign = false;

        if (-1 !== obj_div_data.init_fml.indexOf(cui_equal_sign)) {
            b_init_fml_has_equal_sign = true;
        }

        // 行扫数公式
        var sweep_fomular = "";

        if (b_infml_and_fml_is_equal) {
            if (b_init_fml_has_equal_sign) {
                sweep_fomular = obj_div_data.formula.replace(obj_div_data.init_fml, "");
            } else {
                sweep_fomular = obj_div_data.formula.replace(cui_equal_sign + obj_div_data.init_fml, "");
            }
        }

        //  复制公式值基准
        var copy_fomula_basic = obj_div_data.formula;

        if (b_init_fml_has_equal_sign && b_infml_and_fml_is_equal) {
            copy_fomula_basic = obj_div_data.init_fml;
        }


        // pos_dir ;改变的是top值还是left值;正负极方向
        for (var idx = 0; idx < obj_div_data.loop_num; idx++) {
            var id = "copy_div_" + Number(idx + 1);

            // 若初始公式不等于公式值,则表示修改过扫数值;该模式下复制的数据为根据原始公式复制
            if (b_infml_and_fml_is_equal) {
                // 是否有等于符号
                if (b_init_fml_has_equal_sign) {
                    var init_result = get_val_from_formula_merge(copy_fomula_basic, drag_dir, pos_dir, idx + 1);

                    param_init_fml = init_result.formula;
                    param_foamula = param_init_fml + sweep_fomular;
                } else {
                    param_init_fml++;
                    param_foamula = "=" + param_init_fml + sweep_fomular;
                }
            } else {
                if (b_init_fml_has_equal_sign) {
                    var result = get_val_from_formula_merge(copy_fomula_basic, drag_dir, pos_dir, idx + 1);

                    param_foamula = result.formula;
                } else {
                    param_foamula++;
                }
                param_init_fml = param_foamula;
            }


            // 定义传递参数
            var div_data = {
                id: id,
                top: obj_div_data.top,
                left: obj_div_data.left,
                init_fml: param_init_fml,
                formula: param_foamula,
                val: param_val,
                sweep: obj_div_data.sweep,
                point: obj_div_data.point
            };

            create_virtual_div_copy(div_data);

            //拖拽方向：方向dir为x轴或是y轴
            if (drag_dir) {
                //方向x_y_dir;为下右===true||上左=== false
                if (pos_dir) {
                    obj_div_data.top += obj_step_data.height;
                } else {
                    obj_div_data.top -= obj_step_data.height;
                }
            } else {
                if (pos_dir) {
                    obj_div_data.left += obj_step_data.width;
                } else {
                    obj_div_data.left -= obj_step_data.width;
                }
            }
        }
    }


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    同方向设置一组复制组数内个数
     * 参数:
     *    @param { Promise<Number>} select_num 框选个数
     *    @param { Promise<Number>} copy_num 拖拽复制个数
     * 返回：
     *    @returns { Promise<Array> } 排列个数数组
     * 例子：
     *    NA
     * 备注：
     *    同方向：框选方向和拖拽方向为同一个方向；
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2020.3.14
     *      内容 : 所有代码
     ************************************************************************************************/
    function set_gp_num(select_num, copy_num) {
        // 定义排列数组；
        var arr_sort = new Array(select_num);
        var loop_num = copy_num;
        var idx = 0;

        for (var i = 0; i < loop_num; i++) {
            if ("undefined" === typeof arr_sort[idx]) {
                arr_sort[idx] = 0;
            }

            if (0 !== copy_num) {
                arr_sort[idx] += 1;
                copy_num--;
            }

            idx++;

            if (idx === select_num) {
                idx = 0;
            }
        }

        return arr_sort;
    }

    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *   获取下一个位置
     * 参数:
     *    @param { Promise<String>} str 公式字符串
     *    @param { Promise<Array>} arr 公式分解之后的数据
     *    @param { Promise<Object>} drag_dir 拖拽方向
     *    @param { Promise<Object>} pos_dir 位置方向
     *    @param { Promise<Object>} pos_step 位置步进值对象
     * 返回：
     *    @returns { Promise<Object> } 包含下一个位置值得对象
     * 例子：
     *    NA
     * 备注：
     *    NA
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2020.3.14
     *      内容 : 所有代码
     ************************************************************************************************/
    function get_next_pos(str, arr, drag_dir, pos_dir, pos_step) {
        var new_arr = arr;

        for (var idx = 0; idx < new_arr.length; idx++) {
            var old_pos = new_arr[idx].pos;

            var x_val = old_pos.split(":")[0].split("(")[1];
            var y_val = old_pos.split(":")[1].split(")")[0];

            if (drag_dir) {
                if (pos_dir) {
                    // 下
                    y_val = Number(y_val) + pos_step;
                } else {
                    // 上
                    y_val = Number(y_val) - pos_step;
                }
            } else {
                if (pos_dir) {
                    // 右
                    x_val = Number(x_val) + pos_step;
                } else {
                    // 左
                    x_val = Number(x_val) - pos_step;
                }
            }

            if (1 > Number(x_val) || col < Number(x_val) || 1 > Number(y_val) || row < Number(y_val)) {
                // x_val = 0;
                // y_val = 0;
                // show_info_massage("MC_LANG_POPOUT_WRONG_OSITION_VALUE");
                // return false;
                new_arr[idx].new_pos = "无效位置";
                continue;
            }

            new_arr[idx].new_pos = "(" + x_val + ":" + y_val + ")";
        }

        return replace_position(str, new_arr);
    }


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    递归查找公式值；返回对应的值
     * 参数:
     *    @param { Promise<String>} cur_pos 当前位置值字符串
     *    @param { Promise<String>} str 公式值字符串
     * 返回：
     *    @returns { Promise<String>} 公式值转换之后计算的值  || false
     * 例子：
     *    NA
     * 备注：
     *    返回 false 解释: 进入死循环 || 编辑的位置值格式错误(缺胳膊少腿)
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2020.3.14
     *      内容 : 所有代码
     ************************************************************************************************/
    function find_child_formula(cur_pos, str) {
        if ("string" === typeof str) {
            if (-1 !== str.indexOf("=")) {
                var arr_split = str.split("=");

                if (2 < arr_split.length) {
                    return false;
                }

                str = arr_split[1];
                var arr = find_pos(str);

                if (false === arr) {
                    return false;
                }

                if (0 !== arr.length && arr instanceof Array) {
                    var arr_len = arr.length;

                    for (var idx = 0; idx < arr_len; idx++) {
                        var pos = arr[idx].pos;

                        try {
                            if (pos === cur_pos) {
                                throw Error();
                            }
                        } catch (e) {
                            throw Error(cui_endless_loop);
                        }


                        if (Object.prototype.hasOwnProperty.call(obj_pos_formula, pos)) {
                            var child_formula = obj_pos_formula[pos];

                            arr[idx].val = find_child_formula(cur_pos, child_formula);
                        } else {
                            arr[idx].val = 0;
                        }
                    }
                    var str_calc_loop = replace_position(str, arr);

                    return mc_data_compute(str_calc_loop.calc_val);
                }
                return mc_data_compute(str);
            }
            // 小数点
            var b_decimal = (/\./g).test(str);

            if (b_decimal) {
                return false;
            }
            return str;
        }
        return str;
    }


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    查找字符串内的位置值；
     * 参数:
     *    @param { Promise<String>} str 公式值字符串
     * 返回：
     *    @returns { Promise<Array>} Array ===  找到的位置值数组 || String === 不存在位置值返回原始字符串  || false === 位置字符串格式错误或不存在该位置
     * 例子：
     *    NA
     * 备注：
     *    NA
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2020.3.14
     *      内容 : 所有代码
     ************************************************************************************************/
    function find_pos(str) {
        if ("string" !== typeof str) {
            return false;
        }

        // 存放左边括号
        var leftArr = [];
        // 存放成功数据
        var successArr = [];

        /*遍历字符串*/
        for (var idx = 0; idx < str.length; idx++) {
            if ("(" === str[idx] || "（" === str[idx]) {
                leftArr.push(idx);
            } else if (")" === str[idx] || "）" === str[idx]) {
                if (0 === leftArr.length) {
                    // 位置值错误
                    return false;
                }
                var start = leftArr.pop();
                var pos = str.substring(start, idx + 1);

                if (-1 !== pos.indexOf(":") || -1 !== pos.indexOf("：")) {
                    if (1 !== pos.match(/:|：/g).length) {
                        return false;
                    }

                    if (false === test_pos_is_vaild(pos)) {
                        return false;
                    }
                    // successArr.push(pos);
                    successArr.push({
                        pos: pos,
                        start: start,
                        end: idx + 1
                    });
                }
            }
        }
        // 检查leftArr的长度是否为0
        if (0 !== leftArr.length) {
            // 位置值错误
            // var i = leftArr[0];
            return false;
        }

        if (0 !== successArr.length) {
            return successArr;
        }

        return str;
    }

    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    更新位置值和公式映射的map表
     * 参数:
     *    NA
     * 返回：
     *    NA
     * 例子：
     *    NA
     * 备注：
     *    NA
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2020.3.14
     *      内容 : 所有代码
     ************************************************************************************************/
    function update_formula_map_val() {
        var arr_create_div = arr_create_obj;
        var arr_len = arr_create_div.length;
        var error_info = {
            b_error: false,
            str_error: "错误"
        };

        m_ui_max_fm_val = 0;

        for (var idx = 0; idx < arr_len; idx++) {
            var item = arr_create_div[idx];
            var str_div_type = item.getAttribute("div_type");

            if (str_div_type === get_div_type_val(3)) {
                continue;
            }

            var old_val = item.innerText;
            var formula = item.getAttribute("formula");
            var pos = item.getAttribute("pos");
            // var sweep = item.getAttribute("sweep");
            var point = Number(item.getAttribute("point"));
            var grp = Number(item.getAttribute("group_idx"));
            var point_val = "";
            var calc_val = "";

            try {
                calc_val = find_child_formula(pos, formula);
                if (false === calc_val || 0 >= calc_val) {
                    throw Error("MC_LANG_POPOUT_HAS_INCALID_VALUE");
                }
            } catch (e) {
                error_info.b_error = true;
                error_info.str_error = e.message;
                item.innerText = cui_invalid_value;
                continue;
            }

            // update max value
            if (Number(calc_val) > m_ui_max_fm_val) {
                m_ui_max_fm_val = Number(calc_val);
            }

            if (0 !== point) {
                point_val = "[" + point + "]";
            }

            // 第几组数据
            var grp_idx = Math.ceil(calc_val / obj_external_mod_data.ui_single_grp_num);

            if (grp !== grp_idx) {
                item.setAttribute("group_idx", grp_idx);
                remove_obj_grp_css_class(item);
                if (1 !== grp_idx) {
                    if (grp_idx > obj_external_mod_data.ui_grp_num) {
                        item.classList.add(arr_grp_color[1]);
                    } else {
                        item.classList.add(arr_grp_color[grp_idx]);
                    }
                }
            }

            // 使用计算公式
            var show_val = get_calc_sweep_formula_val(calc_val);
            var new_val = point_val + show_val.val;

            if (old_val !== new_val) {
                item.innerText = new_val;
                // pre_edit === 编辑之前的显示值
                item.pre_edit = new_val;
            }
        }

        if (error_info.b_error) {
            show_info_massage(error_info.str_error);
        }
    }

    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    替换公式的最后一个整数;
     * 参数:
     *    @param { Promise<String>} str 需要替换的字符串
     *    @param { Promise<String>} step 替换的最后一个整数增加的步进值
     * 返回：
     *    @returns { Promise<String>} 返回替换后的字符串
     * 例子：
     *    NA
     * 备注：
     *    NA
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2020.3.14
     *      内容 : 所有代码
     ************************************************************************************************/
    function replace_last_num(str, step) {
        var new_str = "";
        // 匹配整数
        var arr_int = str.match(cui_reg_int);

        if (!arr_int) {
            return str;
        }

        var arr_len = arr_int.length - 1;
        var last_int = arr_int[arr_len];

        var idx = str.lastIndexOf(last_int);
        var sub_str = str.substr(idx);

        var new_val = Number(last_int) + Number(step);
        var str_start = str.slice(0, idx);
        var str_replace = sub_str.replace(last_int, new_val);

        new_str = str_start.concat("", str_replace);
        return new_str;
    }


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    获取公式值
     * 参数:
     *    @param { Promise<String>} str 公式字符串
     *    @param { Promise<Object>} drag_dir 拖拽方向
     *    @param { Promise<Object>} pos_dir 位置方向
     *    @param { Promise<Object>} pos_step 位置步进值对象
     * 返回：
     *    @returns { Promise<Object> } 包含新得公式值对象
     * 例子：
     *    NA
     * 备注：
     *    NA
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2020.3.14
     *      内容 : 所有代码
     ************************************************************************************************/
    function get_val_from_formula_merge(str, drag_dir, pos_dir, pos_step) {
        var str_split = str.split("=")[1];
        var arr_formula = break_down_formula(str_split);
        // var val = 0;
        var new_formula = str;

        // 有值的公式转化为数值
        if (0 !== arr_formula.length && "string" !== typeof arr_formula) {
            var next_pos = get_next_pos(str_split, arr_formula, drag_dir, pos_dir, pos_step);
            // var arr_next_pos = break_down_formula(next_pos.pos);
            // var new_replace_formula = "";

            // if ( 0 !== arr_next_pos.length && "string" !== typeof arr_next_pos) {
            //     new_replace_formula = replace_position(next_pos.pos,arr_next_pos);
            // }

            new_formula = "=" + next_pos.pos;
            // val = mc_data_compute(new_replace_formula.calc_val);
        } else {
            new_formula = "=" + str_split;
            new_formula = replace_last_num(str, pos_step);
            // val = mc_data_compute(str_split);
        }

        return {
            formula: new_formula
            // val: val
        };
    }


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    获取点数据(获取的是页面的数据)
     * 参数:
     *    @param { Promise<Boolean> } b_is_fill 是否是获取填充数据
     * 返回：
     *    @returns { Promise<Array> } 返回相应的点数据 || Boolean === false
     * 例子：
     *    NA
     * 备注：
     *    返回点数据数组时:
     *       填充数据虚点位置在该项数据前面;
     *       应用数据虚点位置在该项数据后面; 应用数据只返回数据组数为第一组的数据
     * 修改:
     *   2020.5.29修改一：不生成虚点数据，直接在点数据对象添加属性invalid_cnt
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2020.4.3
     *      内容 : 所有代码
     ************************************************************************************************/
    function get_point_data(b_is_fill) {
        if ("boolean" !== typeof b_is_fill) {
            return false;
        }

        // 应用数据
        var arr_apply_data = [];
        // 填充数据
        var arr_fill_data = [];
        var arr_create_div = arr_create_obj;
        var arr_len = arr_create_div.length;

        if (0 === arr_len) {
            return [];
        }

        for (var idx = 0; idx < arr_len; idx++) {
            var item = arr_create_div[idx];
            var x_val = Number(item.getAttribute("x"));
            var y_val = Number(item.getAttribute("y"));
            var point = Number(item.getAttribute("point"));
            var sweep = Number(item.getAttribute("sweep"));
            var grp_idx = Number(item.getAttribute("group_idx"));
            var val = item.innerText;
            // var arr_int = val.match(cui_reg_int);
            var real_val = val;

            if (0 !== point) {
                var str_point = "[" + point + "]";

                real_val = val.replace(str_point, "");
            }

            var val_split = real_val;
            var idx_val = "";

            if (-1 !== real_val.indexOf("_")) {
                val_split = real_val.split("_");
                sweep = Number(val_split[0]);
                idx_val = val_split[1];
            }

            var obj_item_data = {
                idx: idx_val,
                sweep: sweep,
                x: x_val,
                y: y_val,
                invalid_cnt: point,
                grp_idx: grp_idx
            };

            // 应用数据只获取第一组数据
            if (1 === grp_idx) {
                arr_apply_data.push(obj_item_data);
            }

            arr_fill_data.push(obj_item_data);
        }

        // 获取填充数据
        if (b_is_fill) {
            return arr_fill_data;
        }
        return arr_apply_data;
    }


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    获取填充数据；
     * 参数:
     *    NA
     * 返回：
     *    NA
     * 例子：
     *    @returns { Promise<Array> } 返回填充数据数组
     * 备注：
     *    返回数据虚点位置在前面;
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2020.4.2
     *      内容 : 所有代码
     ************************************************************************************************/
    this.get_fill_data = function () {
        return get_point_data(true);
    };


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    获取应用数据；
     * 参数:
     *    NA
     * 返回：
     *    @returns { Promise<Array> } 返回应用数据数组
     * 例子：
     *    NA
     * 备注：
     *    返回数据虚点位置在后面;
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2020.4.2
     *      内容 : 所有代码
     ************************************************************************************************/
    this.get_apply_data = function () {
        return get_point_data(false);
    };


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    设置走点；填充
     * 参数:
     *    @param { Promise<Array> } arr 需要填充渲染的数据集合
     * 返回：
     *    @returns { Promise<Boolean> } 操作成功 === true || 操作失败 === false
     * 例子：
     *    NA
     * 备注：
     *    NA
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2020.4.2
     *      内容 : 所有代码
     ************************************************************************************************/
    this.set_trace_point = function (arr) {
        if (!Array.isArray(arr)) {
            return false;
        }

        var len = arr.length;

        if (0 === len) {
            return false;
        }


        // 渲染之前先保存之前已生成的div对象集合；深拷贝
        var copy_arr_obj = arr_create_obj.slice(0);

        // 清空
        arr_block_data_change_sub = [];

        for (var idx = 0; idx < len; idx++) {
            var item = arr[idx];

            var id = "rander_div_" + idx + "_" + (Math.random() * 1000).toFixed();
            // var formula = item.idx;
            var x_val = Number(item.x);
            var y_val = Number(item.y);
            var sweep = item.sweep;
            var point = Number(item.invalid_cnt);
            var grp_idx = Number(item.grp_idx);

            if (!juged_has_existd_obj(copy_arr_obj, x_val, y_val)) {
                ui_click_num++;
                var show_val = sweep + "_" + item.idx;
                var formula = obj_external_mod_data.ui_single_sweep_len * (sweep - 1) + Number(item.idx) + obj_external_mod_data.ui_single_grp_num * (grp_idx - 1);

                create_div_render(x_val, y_val, formula, id, point, grp_idx, show_val);
            }
        }

        // 堆栈
        multiple_data_val_chg();
        // 更新数据
        // update_formula_map_val();

        return true;
    };


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    填充块
     * 参数:
     *    @param { Promise<Number> } ui_i 外部所有渲染数据的下标
     *    @param { Promise<Object> } obj 渲染的块数据
     *    @param { Promise<Array> } arr_block 渲染之前保存的块数据
     *    @param { Promise<Array> } b_push_stack 是否直接堆栈
     * 返回:
     *    @returns { Promise<Boolean> } 设置成功 || 失败
     * 例子:
     *    NA
     * 备注:
     *    NA
     * 修改:
     *    1. 类型 : 创建
     *       作者 : 巫昭雯
     *       时间 : 2020-10-14
     *       内容 : 所有代码
    ************************************************************************************************/
    this.tb_io_fill_block = function (ui_i, obj, arr_block, b_push_stack) {
        if ("number" !== typeof ui_i || "[object Object]" !== Object.prototype.toString.call(obj) || "[object Array]" !== Object.prototype.toString.call(arr_block)) {
            return false;
        }

        if (obj) {
            var item = obj;
            var x_val = Number(item.x);
            var y_val = Number(item.y);

            if (!juged_has_existd_obj(arr_block, x_val, y_val)) {
                ui_click_num++;

                var id = "rander_div_" + ui_i + "_" + (Math.random() * 1000).toFixed() + new Date().getTime();
                var formula = ui_i + 1;
                var point = Number(item.invalid_cnt);
                var obj_res = get_calc_sweep_formula_val(formula);
                var show_val = obj_res.val;
                var grp_idx = Math.ceil(formula / obj_external_mod_data.ui_single_grp_num);

                if (Number(formula) > m_ui_max_fm_val) {
                    m_ui_max_fm_val = Number(formula);
                }

                create_div_render(x_val, y_val, formula, id, point, grp_idx, show_val);

                if (b_push_stack) {
                    multiple_data_val_chg();
                }
            }

            return true;
        }

        return false;
    };

    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    判断填充数据是否存在已生成数据
     * 参数:
     *    @param { Promise<Array> } arr_create_div 已存在生成对象集合
     *    @param { Promise<Number> } x_val 判断的x轴值
     *    @param { Promise<Number> } y_val 判断的y轴值
     * 返回：
     *    @returns { Promise<Boolean> } 存在 === true || 不存在 === false
     * 例子：
     *    NA
     * 备注：
     *    NA
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2020.4.18
     *      内容 : 所有代码
     ************************************************************************************************/
    function juged_has_existd_obj(arr_create_div, x_val, y_val) {
        // var arr_create_div = arr;
        var arr_create_div_len = arr_create_div.length;

        for (var idx = 0; idx < arr_create_div_len; idx++) {
            var item = arr_create_div[idx];

            if (Number(item.getAttribute("x")) === x_val && Number(item.getAttribute("y")) === y_val) {
                return true;
            }
        }
        return false;
    }


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    添加渲染div
     * 参数:
     *    @param { Promise<String> } x_val 创建渲染div的x轴位置值
     *    @param { Promise<String> } y_val 创建渲染div的y轴位置值
     *    @param { Promise<String> } formula 创建渲染div的公式值
     *    @param { Promise<String> } id 创建渲染div的id值
     *    @param { Promise<Number> } point 创建渲染div的虚点值
     *    @param { Promise<String> } grp_idx 创建渲染div的数据组数
     *    @param { Promise<String> } show_val 创建渲染div的显示值
     * 返回：
     *    NA
     * 例子：
     *    NA
     * 备注：
     *    NA
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2020.4.2
     *      内容 : 所有代码
     ************************************************************************************************/
    function create_div_render(x_val, y_val, formula, id, point, grp_idx, show_val) {
        var real_id = id_name + "_" + id + "_" + (Math.random() * 1000).toFixed();
        // var is_edit = false;
        // var b_choosr = true;
        var pos_val = "(" + x_val + ":" + y_val + ")";
        var left = each_rect_width * (x_val - 1);
        var top = each_rect_height * (y_val - 1);
        var init_fml = formula;
        var sweep = ui_sweep_num;
        var innerText = show_val;

        if (0 !== point) {
            innerText = "[" + point + "]" + innerText;
        }

        var str_class_name = "click_create_div ";

        if (1 !== grp_idx) {
            if (grp_idx > obj_external_mod_data.ui_grp_num) {
                str_class_name += arr_grp_color[1];
            } else {
                str_class_name += arr_grp_color[grp_idx];
            }
        }

        var str_click_div = "<div id='" + real_id + "' div_type='" + get_div_type_val(0) + "' is_choose='true' tabindex='0' contenteditable='is_edit' class='" + str_class_name + "' style='top:" + top + "px;left:" + left + "px;width:" + each_rect_width + "px;height:" + each_rect_height +
            "px;line-height:" + each_rect_height + "px;' x='" + x_val + "' y='" + y_val + "' pos='" + pos_val + "' init_fml = '" + init_fml + "'  formula='" + formula +
            "'sweep='" + sweep + "' point='" + point + "'group_idx='" + grp_idx + "' show_val='" + show_val + "'>" + innerText + "</div>";

        $obj_svg_jq.append(str_click_div);

        // var obj_div_click = document.getElementById(real_id);

        operation_of_new_create_point(document.getElementById(real_id));

        // 更新位置对象
        obj_pos_formula[pos_val] = formula;

        // 定义堆栈数据
        var param_data = {
            b_is_init: true,
            pos: pos_val,
            init_fml: formula,
            formula: formula,
            point: point,
            sweep: sweep,
            id: real_id
        };

        // 堆栈
        set_val_change_action(param_data);
    }

    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    检验位置值是否有效;
     * 参数:
     *    @param { Promise<String>} str 存在位置值的字符串
     * 返回：
     *    @returns { Promise<Boolean>}  true || false
     * 例子：
     *    NA
     * 备注：
     *    有效的位置值范围x轴和y轴值在 1 和 最大值之间 (包括1和最大值)
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2020.3.14
     *      内容 : 所有代码
     ************************************************************************************************/
    function test_pos_is_vaild(str) {
        var arr_int = str.match(cui_reg_int);
        var x_val = 0;
        var y_val = 0;

        if (arr_int[0] && arr_int[1]) {
            x_val = arr_int[0];
            y_val = arr_int[1];
        }

        if (1 > Number(x_val) || col < Number(x_val) || 1 > Number(y_val) || row < Number(y_val)) {
            return false;
        }
        return true;
    }


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    设置放大缩小;
     * 参数:
     *    @param { Promise<Number>}  ui_zoom  1 === 缩小 || other === 放大
     *    @param { Promise<Object>}   step 可选参数；放大缩小的步进值
     * 返回：
     *    NA
     * 例子：
     *    NA
     * 备注：
     *    默认缩放步进值为width === 3 && height === 1
     *    缩放步进参数格式：对象内必须有width,height的键；例子：{ width: 3,height: 1 }
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2020.3.23
     *      内容 : 所有代码
     ************************************************************************************************/
    this.set_svg_zoom = function (ui_zoom, step) {
        var each_rect = document.getElementById(pattern_rect_id);
        var pattern = document.getElementById(pattern_id);
        var arr_obj_head = document.getElementById(mc_head_wrap_id).getElementsByClassName("rect_border_left");
        var arr_obj_nav = document.getElementById(mc_nav_wrap_id).getElementsByClassName("rect_border_top");

        // 保存缩放最大最小边界值
        var rect_width = Number(each_rect.getAttribute("width"));
        var rect_height = Number(each_rect.getAttribute("height"));


        // 定义最小值
        var b_min_size = 30 === rect_width || 15 === rect_height;

        if (b_min_size && 1 === ui_zoom) {
            return;
        }

        // 默认步进值
        var def_step = {
            width: 3,
            height: 1
        };

        step = step || def_step;

        if (1 === ui_zoom) {
            each_rect_width -= step.width;
            each_rect_height -= step.height;
        } else {
            each_rect_width += step.width;
            each_rect_height += step.height;
        }

        // 保存生成点边界值
        rect_x_width = each_rect_width * (col - 1);
        rect_y_height = each_rect_height * (row - 1);

        // 设置外部svg宽高
        svg_width = each_rect_width * col;
        svg_height = each_rect_height * row;

        svg_item_content.setAttribute("width", svg_width);
        svg_item_content.setAttribute("height", svg_height);
        arr_obj_head[0].parentNode.setAttribute("width", svg_width + 1);
        arr_obj_nav[0].parentNode.setAttribute("height", svg_height + 1);

        var pattern_width = each_rect_width / svg_width;
        var pattern_height = each_rect_height / svg_height;
        var rect_stroke_dasharray = each_rect_width + "," + (Number(each_rect_width) + Number(each_rect_height));

        each_rect.setAttribute("width", each_rect_width);
        each_rect.setAttribute("height", each_rect_height);
        each_rect.setAttribute("stroke-dasharray", rect_stroke_dasharray);

        pattern.setAttribute("width", pattern_width);
        pattern.setAttribute("height", pattern_height);

        var obj_bgc_x = svg_item_content.querySelectorAll(".bgc_hl_x");
        var obj_bgc_y = svg_item_content.querySelectorAll(".bgc_hl_y");

        set_bgc_hl_zoom(obj_bgc_x, "X");
        set_bgc_hl_zoom(obj_bgc_y, "Y");

        set_nav_zoom(arr_obj_nav, true);
        set_nav_zoom(arr_obj_head, false);

        // 设置hover块
        obj_div_hover_content.style.cssText += "top:0px;left: 0px;width:" + each_rect_width + "px;height:" + each_rect_height + "px;display:block";
        obj_div_hover_head.style.cssText += "left:0px;display:block;width:" + each_rect_width + "px";
        obj_div_hover_nav.style.cssText += "top:0px;display:block;height:" + each_rect_height + "px";

        set_create_div_style();
        set_style_after_drag("放大缩小", ui_selected_xy);
    };


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    设置导航缩放;
     * 参数:
     *    @param { Promise<Object>}  arr_obj 导航栏dom对象
     *    @param { Promise<Boolean>} b_is_nav  是否为左边导航；
     * 返回：
     *    NA
     * 例子：
     *    NA
     * 备注：
     *    NA
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2020.3.23
     *      内容 : 所有代码
     ************************************************************************************************/
    function set_nav_zoom(arr_obj, b_is_nav) {
        if ("boolean" !== typeof b_is_nav || "object" !== typeof arr_obj) {
            return;
        }
        var len = arr_obj.length;

        var x_y = "x";
        var str_width_height = "width";
        var width_height = each_rect_width;

        if (b_is_nav) {
            x_y = "y";
            str_width_height = "height";
            width_height = each_rect_height;
        }

        for (var idx_nav = 0; idx_nav < len; idx_nav++) {
            var item = arr_obj[idx_nav];

            var rect_x = idx_nav * width_height + 0.5;
            var text_x = rect_x + (width_height / 2);

            item.nextElementSibling.setAttribute(x_y, text_x);
            item.setAttribute(x_y, rect_x);
            item.setAttribute(str_width_height, width_height);
        }
    }


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    重新设置生成块div样式;
     * 参数:
     *    NA
     * 返回：
     *    NA
     * 例子：
     *    NA
     * 备注：
     *    NA
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2020.3.23
     *      内容 : 所有代码
     ************************************************************************************************/
    function set_create_div_style() {
        // var arr_create_div = document.getElementsByClassName("click_create_div");
        var arr_create_div = mc_tb_wrap_div.getElementsByClassName("click_create_div");
        var arr_len = arr_create_div.length;

        if (0 === arr_len) {
            return;
        }

        for (var idx = 0; idx < arr_len; idx++) {
            var item = arr_create_div[idx];

            if (item) {
                var div_top = each_rect_height * (Number(item.getAttribute("y")) - 1);
                var div_left = each_rect_width * (Number(item.getAttribute("x")) - 1);

                item.style.cssText += "top:" + div_top + "px;left:" + div_left + "px;width:" + each_rect_width + "px;height:" + each_rect_height + "px;line-height:" + each_rect_height + "px;";
            }
        }
    }


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    重新走点;
     * 参数:
     *    NA
     * 返回：
     *    NA
     * 例子：
     *    NA
     * 备注：
     *    NA
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2020.3.23
     *      内容 : 所有代码
     ************************************************************************************************/
    this.reset_create_div = function () {
        clear_data_fun();
        mc_svg_tb_hardware_callback(1);
    };


    this.tb_io_reset_data = function () {
        clear_data_fun();
    };


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    清空数据,重置;
     * 参数:
     *    NA
     * 返回：
     *    NA
     * 例子：
     *    NA
     * 备注：
     *    清空内置数据；包括生成对象； 框选对象；位置映射对象；虚点数据；点击计数；数据变动数组等
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2020.4.16
     *      内容 : 所有代码
     ************************************************************************************************/
    function clear_data_fun() {
        $("#" + mc_svg_id + " .click_create_div").remove();

        // 清空保存对象
        ui_click_num = 0;
        ui_click_num_virtual = 0;
        m_ui_max_fm_val = 0;
        // ui_point_show_pos = 0;
        ui_virtual_point = 0;
        set_hover_div_val(ui_virtual_point);
        obj_pos_formula = {};
        arr_create_obj = [];
        arr_box_select_obj = [];
        set_edit_box_val("");
        set_pos_div_val("");
        set_style_after_drag("清空数据", ui_selected_xy);

        // 堆栈数据
        arr_block_data_change = [];
        arr_block_data_change_sub = [];

        // 编辑选择框(红色边框)
        var $obj_edit_sel = $(".edit_select");

        if (0 !== $obj_edit_sel.length) {
            $obj_edit_sel.remove();
        }
    }


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    设置扫数;
     * 参数:
     *    @param { Promise<String>}  val 行扫数值 || Number
     * 返回：
     *    NA
     * 例子：
     *    NA
     * 备注：
     *    NA
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2020.3.24
     *      内容 : 所有代码
     ************************************************************************************************/
    this.set_sweep_num = function (val) {
        ui_sweep_num = val;

        // 清空
        arr_block_data_change_sub = [];

        // 设置框选的扫数
        var arr_len = arr_box_select_obj.length;

        for (var idx = 0; idx < arr_len; idx++) {
            var item = arr_box_select_obj[idx];

            // 虚点模式
            if (item.getAttribute("div_type") === get_div_type_val(3)) {
                continue;
            }

            var old_val = item.innerText;
            var new_val = replace_sweep_val(old_val, ui_sweep_num);
            var init_fml = item.getAttribute("init_fml");

            // 保存更改动作
            var formula = item.getAttribute("formula");
            var pos_val = item.getAttribute("pos");
            var point = item.getAttribute("point");

            // 若设置的扫数与原扫数相同
            if (-1 !== old_val.indexOf("_")) {
                if (Number(old_val.split("_")[0]) === Number(ui_sweep_num)) {
                    continue;
                }
            }

            // 求新的公式
            var sweep_step = get_update_sweep_step(pos_val, formula, init_fml, Number(ui_sweep_num));

            // 设置新公式值
            item.setAttribute("formula", sweep_step);
            // 设置扫数
            item.setAttribute("sweep", ui_sweep_num);
            item.innerText = new_val;

            // 更新对象
            obj_pos_formula[pos_val] = sweep_step;

            var param_data = {
                b_is_init: false,
                pos: pos_val,
                init_fml: init_fml,
                formula: sweep_step,
                point: point,
                sweep: ui_sweep_num,
                id: item.id
            };

            // 堆栈
            set_val_change_action(param_data);
        }

        multiple_data_val_chg();
        update_formula_map_val();

        // 设置编辑框公式值
        if (1 === arr_len) {
            var formula_val = arr_box_select_obj[0].getAttribute("formula");

            set_edit_box_val(formula_val);
        }
    };


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    获取设置行扫数之后的新的公式值;  获取需要更新的扫数增减值;
     * 参数:
     *    @param { Promise<String> } pos 位置值
     *    @param { Promise<String> } formula 公式值
     *    @param { Promise<String> } init_fml 初始公式值
     *    @param { Promise<Number> } sweep 设置的行扫数
     * 返回：
     *    @returns { Promise<Object> } 新的公式值
     * 例子：
     *    NA
     * 备注：
     *    返回新的公式值有等号
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2020.5.5
     *      内容 : 所有代码
     ************************************************************************************************/
    function get_update_sweep_step(pos, formula, init_fml, sweep) {
        var show_val = get_calc_sweep_formula_val(find_child_formula(pos, init_fml));
        var old_sweep = show_val.sweep;

        var step = Math.abs(sweep - old_sweep);
        var str_add_sweep = obj_external_mod_data.ui_single_sweep_len + "*" + step;

        var new_formula = init_fml;

        if (sweep > old_sweep) {
            new_formula += ("+" + str_add_sweep);
        } else {
            new_formula += ("-" + str_add_sweep);
        }

        if (-1 === init_fml.indexOf("=")) {
            return "=" + new_formula;
        }

        return new_formula;
    }


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    替换扫数;
     * 参数:
     *    @param { Promise<String>} str 包含扫数的字符串值
     *    @param { Promise<String>} replace_val 需要替换的扫数值
     * 返回：
     *    @returns { Promise<String> } 替换后得值
     * 例子：
     *    NA
     * 备注：
     *    NA
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2020.3.24
     *      内容 : 所有代码
     ************************************************************************************************/
    function replace_sweep_val(str, replace_val) {
        if ("string" !== typeof str) {
            str = JSON.stringify(str);
        }
        var val = "";

        if (-1 !== str.indexOf("_")) {
            var str_split = str.split("_")[0];
            var str_rest = str.split("_")[1];
            var point_last_idx = str_split.indexOf("]");

            if (-1 !== point_last_idx) {
                val = str_split.substring(0, point_last_idx + 1) + replace_val + "_" + str_rest;
            } else {
                val = replace_val + "_" + str_rest;
            }
        }

        return val;
    }


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    设置虚点;
     * 参数:
     *    @param { Promise<String>} val 虚点值
     * 返回：
     *    NA
     * 例子：
     *    NA
     * 备注：
     *    NA
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2020.3.24
     *      内容 : 所有代码
     ************************************************************************************************/
    this.set_virtual_point_num_backup = function (val) {
        if ("undefined" === typeof val) {
            return;
        }
        val = Number(val);
        ui_virtual_point += val;

        var arr_len = arr_create_obj.length;
        var str_new_point = "";

        if (0 !== arr_len && 0 !== val) {
            var obj_cur = arr_create_obj[arr_len - 1];
            var old_point = Number(obj_cur.getAttribute("point"));
            var old_val = obj_cur.innerText;
            var new_point_val = old_point + val;
            var new_val = "";

            str_new_point = "[" + new_point_val + "]";

            if (0 !== old_point) {
                var replace_val = "[" + old_point + "]";

                new_val = old_val.replace(replace_val, str_new_point);
            } else {
                new_val = str_new_point + old_val;
            }

            obj_cur.setAttribute("point", new_point_val);
            obj_cur.innerText = new_val;

            // 保存更改动作
            var formula = obj_cur.getAttribute("formula");
            var pos_val = obj_cur.getAttribute("pos");
            var sweep = obj_cur.getAttribute("sweep");

            var param_data = {
                b_is_init: false,
                pos: pos_val,
                init_fml: formula,
                formula: formula,
                point: new_point_val,
                sweep: sweep,
                id: obj_cur.id
            };

            set_val_change_action(param_data, multiple_data_val_chg);
        } else {
            // ui_point_show_pos = 1;
        }
    };


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    接口函数； 点击插入虚点按钮设置虚点
     * 参数:
     *    @param { Promise<String> } val
     * 返回:
     *    NA
     * 例子:
     *    NA
     * 备注:
     *    NA
     * 修改:
     *    1. 类型 : 创建
     *       作者 : 巫昭雯
     *       时间 : 2020.3.24
     *       内容 : 所有代码
    ************************************************************************************************/
    this.set_virtual_point_num = function (val) {
        if ("undefined" === typeof val) {
            return;
        }
        ui_virtual_point += Number(val);

        // 如果没有生成点的时候设置虚点的话就直接入栈
        if (0 === arr_create_obj.length) {
            var idx = get_g_obj_idx(id_name);

            if (false !== idx) {
                var arr_param = new format_data_g_svg_obj(idx, true);

                mc_svg_on_val_chg_callback(arr_param);
            }
        }

        if (0 !== ui_virtual_point) {
            set_hover_div_val(ui_virtual_point);
        }
    };


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    获取虚点值
     * 参数:
     *    NA
     * 返回:
     *    @returns { Promise<Number> } 返回当前表格对象的虚点值
     * 例子:
     *    NA
     * 备注:
     *    NA
     * 修改:
     *    1. 类型 : 创建
     *       作者 : 巫昭雯
     *       时间 : 2020-10-26
     *       内容 : 所有代码
    ************************************************************************************************/
    this.get_virtual_point_num = function () {
        return Number(ui_virtual_point);
    };


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    设置虚点的值；修改当前虚点保存的值和只是设置显示虚点的hover div内的值；跟其他数据不相关；
     * 参数:
     *    @param { Promise<Number> } val 设置虚点值; 格式为 number || string
     * 返回:
     *    @returns { Promise<Boolean> } true || false
     * 例子:
     *    NA
     * 备注:
     *    NA
     * 修改:
     *    1. 类型 : 创建
     *       作者 : 巫昭雯
     *       时间 : 2020-10-26
     *       内容 : 所有代码
    ************************************************************************************************/
    this.set_vpoint_val = function (val) {
        if ("undefined" === typeof val) {
            return false;
        }

        ui_virtual_point = val;

        if (0 === val) {
            obj_div_hover_content.innerText = "";
            return true;
        }

        obj_div_hover_content.innerText = "[" + val + "]";
        return true;
    };


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    设置虚点走点模式
     * 参数:
     *    @param { Promise< Boolean > } b
     * 返回:
     *    @returns { Promise< Boolean > } true || false === 传参格式错误
     * 例子:
     *    NA
     * 备注:
     *    NA
     * 修改:
     *    1. 类型 : 创建
     *       作者 : 巫昭雯
     *       时间 : 2020-07-29
     *       内容 : 所有代码
    ************************************************************************************************/
    this.set_virtual_point_mode = function (b) {
        if ("boolean" !== typeof b) {
            return false;
        }

        b_is_virtual_point_mode = b;

        return true;
    };


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    设置内容hover块的值;
     * 参数:
     *    @param { Promise<Number> } val 设置的虚点值
     * 返回：
     *    NA
     * 例子：
     *    NA
     * 备注：
     *    改hover块的值代表虚点值
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2020.6.2
     *      内容 : 所有代码
     ************************************************************************************************/
    function set_hover_div_val(val) {
        if ("undefined" === typeof val) {
            return;
        }

        if (0 === val) {
            obj_div_hover_content.innerText = "";
            ui_virtual_point = 0;
            update_g_vpoint_val(ui_virtual_point);
            return;
        }

        obj_div_hover_content.innerText = "[" + val + "]";
        update_g_vpoint_val(ui_virtual_point);
    }

    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    获取生成表格容器的id
     * 参数:
     *    NA
     * 返回：
     *    @returns { Promise<String> } 返回表格容器的id
     * 例子：
     *    NA
     * 备注：
     *    NA
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2020.4.3
     *      内容 : 所有代码
     ************************************************************************************************/
    this.get_container_id = function () {
        return id_name;
    };


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    获取生成的div总个数
     * 参数:
     *    NA
     * 返回：
     *    @returns { Promise<String> } 生成的div总个数
     * 例子：
     *    NA
     * 备注：
     *    NA
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2020.4.3
     *      内容 : 所有代码
     ************************************************************************************************/
    this.get_create_num = function () {
        return arr_create_obj.length;
    };


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    高级走点设置表格格子颜色;
     * 参数:
     *    @param { Promise<String> } str 参数颜色值
     * 返回：
     *    @returns { Promise<Boolean> }
     * 例子：
     *    参数颜色值： "red" || "#eee"
     * 备注：
     *    参数颜色值支持格式为css样式格式；
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2020.4.3
     *      内容 : 所有代码
     ************************************************************************************************/
    this.set_select_table_bg = function (str) {
        if ("string" !== typeof str) {
            return false;
        }
        document.getElementById(select_all_div_id).style.background = str;
        return true;
    };


    this.remove_current_tag = function () {
        document.getElementById(select_all_div_id).innerHTML = "";
        // document.getElementById(select_all_div_id).classList.remove("select_cur_div");
    };

    this.set_current_tag = function () {
        // add currect tag
        document.getElementById(select_all_div_id).innerHTML = "*";
        // document.getElementById(select_all_div_id).classList.add("select_cur_div");
    };


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    回撤数据
     * 参数:
     *    NA
     * 返回：
     *    @returns { Promise<Boolean>} 操作成功 || 操作失败 || 1 === 回撤虚点(堆栈不弹出)
     * 例子：
     *    NA
     * 备注：
     *    NA
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2020.4.16
     *      内容 : 所有代码
     ************************************************************************************************/
    this.retreat_data = function () {
        var arr_data = arr_block_data_change;
        var arr_data_len = arr_data.length;

        // 如果存在虚点先回撤虚点
        if (0 !== ui_virtual_point) {
            // 数据回撤时带有虚点则先回撤虚点在把数据push进去;
            set_hover_div_val(0);
            return 1;
        }

        if (0 === arr_data_len) {
            return false;
        }

        // 堆栈二维数组的最后一条数据集合；
        var arr_last_data = arr_block_data_change.pop();
        var arr_last_data_len = arr_last_data.length;

        if (0 === arr_last_data_len) {
            return false;
        }

        // 测试函数
        if (1 === arr_last_data_len) {
            mc_svg_tb_hardware_callback(3, true);
        }

        for (var idx = 0; idx < arr_last_data_len; idx++) {
            var last_data = arr_last_data.pop();
            var last_pos = last_data.pos;
            var del_obj = document.getElementById(last_data.div_id);

            if (del_obj && "undefined" === typeof del_obj) {
                return false;
            }

            // b_is_init 表示是否是初始数据；true === 删除该点 || false === 修改数据
            if (last_data.b_is_init) {
                // 删除公式的映射对象
                if (Object.prototype.hasOwnProperty.call(obj_pos_formula, last_pos)) {
                    delete obj_pos_formula[last_pos];
                }
                var obj_pop = arr_create_obj.pop();

                if ("undefined" !== typeof obj_pop) {
                    // 回撤上一个虚点.只有单个数据回撤才设置
                    if (1 === arr_last_data_len) {
                        ui_virtual_point = last_data.point;
                        set_hover_div_val(ui_virtual_point);
                    }

                    // 清除数据; 兼容ie
                    del_obj.parentElement.removeChild(del_obj);
                    ui_click_num--;

                    // 清空之后;重置数据
                    if (0 === arr_create_obj.length) {
                        arr_box_select_obj = [];
                        set_edit_box_val("");
                        set_pos_div_val("");
                        if (0 === ui_virtual_point) {
                            clear_data_fun();
                        }
                    }
                }
            } else {
                var set_data = get_pre_stack_data(last_data.pos);

                // 更新数据
                del_obj.setAttribute("formula", set_data.formula);
                del_obj.setAttribute("init_fml", set_data.init_fml);
                del_obj.setAttribute("sweep", set_data.sweep);
                del_obj.setAttribute("point", set_data.point);
                obj_pos_formula[last_data.pos] = set_data.formula;

                // 添加提示动画
                del_obj.classList.add("block_val_chg_animation");
            }
        }


        // 提取堆栈保存的框选对象
        var arr_data_len_new = arr_data.length;

        // 设置回撤之后保存的框选样式
        set_retreat_box_selecte_style(arr_data_len_new);

        // 取消动画
        setTimeout(remove_obj_animation_class, 1000);

        // 设置框选块的公式值显示在编辑框内
        if (0 !== arr_box_select_obj.length) {
            var new_edit_val = arr_box_select_obj[0].getAttribute("formula");

            set_edit_box_val(new_edit_val);
        }

        // 更新公式
        update_formula_map_val();

        return true;
    };


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    移除对象动画类；取消掉数据变动时添加的动画类
     * 参数:
     *    NA
     * 返回：
     *    NA
     * 例子：
     *    NA
     * 备注：
     *    NA
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2019.4.18
     *      内容 : 所有代码
     ************************************************************************************************/
    function remove_obj_animation_class() {
        var arr_obj_chg = mc_svg.getElementsByClassName("block_val_chg_animation");
        var arr_len = arr_obj_chg.length;

        for (var arr_idx = 0; arr_idx < arr_len; arr_idx++) {
            var item = arr_obj_chg[0];

            item.classList.remove("block_val_chg_animation");
        }
    }


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *   设置回撤框选样式(堆栈保存的框选数据)
     * 参数:
    *    @param { Promise<Number> } new_len 新框选对象长度
     * 返回：
     *    NA
     * 例子：
     *    NA
     * 备注：
     *    NA
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2019.4.18
     *      内容 : 所有代码
     ************************************************************************************************/
    function set_retreat_box_selecte_style(new_len) {
        if ("number" !== typeof new_len) {
            return;
        }

        if (0 !== new_len) {
            var arr_box_obj = arr_block_data_change[new_len - 1][0].cur_arr_box_select;

            remove_selected_div_class();
            set_box_selecte_style(arr_box_obj);

            // 判断方向
            if (2 <= arr_box_obj.length) {
                var obj_one = arr_box_obj[0];
                var obj_two = arr_box_obj[1];

                if (obj_one.offsetTop === obj_two.offsetTop) {
                    ui_selected_xy = 0;
                } else if (obj_one.offsetLeft === obj_two.offsetLeft) {
                    ui_selected_xy = 1;
                }
            }

            // 框选对象赋值
            arr_box_select_obj = arr_box_obj;
            set_style_after_drag("撤回设置框选", ui_selected_xy);
        } else {
            set_style_after_drag("撤回设置框选没有框选数据了", ui_selected_xy);
        }
    }


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    设置框选样式；撤回数据的框选样式
     * 参数:
    *    @param { Promise<Array> } arr 框选对象集合
     * 返回：
     *    NA
     * 例子：
     *    NA
     * 备注：
     *    NA
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2019.4.18
     *      内容 : 所有代码
     ************************************************************************************************/
    function set_box_selecte_style(arr) {
        var arr_len = arr.length;

        for (var idx = 0; idx < arr_len; idx++) {
            var item = arr[idx];

            if (item) {
                var class_list = item.classList;

                class_list.add("box_selection_creat_div");
            }
        }
    }

    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    获取上一组堆栈的数据
     * 参数:
    *    @param { Promise<String> } pos 当前堆栈的位置值
     * 返回：
    *    @returns { Promise<Object> } 找到的数据 || true === 没有找到
     * 例子：
     *    NA
     * 备注：
     *    NA
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2019.4.18
     *      内容 : 所有代码
     ************************************************************************************************/
    function get_pre_stack_data(pos) {
        var arr_out = arr_block_data_change;
        var arr_out_len = arr_block_data_change.length;
        // 此时最后一组数据已经弹出
        var start_idx_out = arr_out_len - 1;

        for (var idx_out = start_idx_out; 0 <= idx_out; idx_out--) {
            var arr_in = arr_out[idx_out];
            var arr_in_len = arr_in.length;
            var start_idx_in = arr_in_len - 1;

            for (var idx_in = start_idx_in; 0 <= idx_in; idx_in--) {
                var item_in = arr_in[idx_in];

                if (item_in.pos === pos) {
                    return item_in;
                }
            }
        }

        return true;
    }


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    设置值改变操作:(生成或改变都需要调用)回撤使用
     * 参数:
     *    @param { Promise<Object>} data 数据变动对象
     *    @param { Promise<Function>} callback_fun_name 回调函数
     * 返回：
     *    NA
     * 例子：
     *    NA
     * 备注：
     *    NA
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2020.4.16
     *      内容 : 所有代码
     * ************************************************************************************************/
    function set_val_change_action(data, callback_fun_name) {
        var val = new obj_block_data(data);

        arr_block_data_change_sub.push(val);

        if ("function" === typeof callback_fun_name) {
            callback_fun_name();
        }
    }


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    多个数据变动;堆栈
     * 参数:
     *    NA
     * 返回：
     *    NA
     * 例子：
     *    NA
     * 备注：
     *    NA
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2019.4.18
     *      内容 : 所有代码
     ************************************************************************************************/
    function multiple_data_val_chg() {
        if (0 === arr_block_data_change_sub.length) {
            return;
        }

        arr_block_data_change.push(arr_block_data_change_sub);
        arr_block_data_change_sub = [];

        var idx = get_g_obj_idx(id_name);

        if (false !== idx) {
            var arr_param = new format_data_g_svg_obj(idx);

            mc_svg_on_val_chg_callback(arr_param);
        }
    }


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    构造全局svg对象格式
     * 参数:
     *    @param { Promise<Number> } idx 全局svg对象集合的下标
     *    @param { Promise<Boolean> } b_vpoint_stack 是否为虚点堆栈
     * 返回：
     *    NA
     * 例子：
     *    NA
     * 备注：
     *    NA
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2019.4.18
     *      内容 : 所有代码
     ************************************************************************************************/
    function format_data_g_svg_obj(idx, b_vpoint_stack) {
        if ("number" !== typeof idx) {
            return;
        }

        this.table_idx = idx;
        this.b_vpoint_stack = b_vpoint_stack || false;
    }


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    设置焦点块
     * 参数:
     *    NA
     * 返回：
     *    NA
     * 例子：
     *    NA
     * 备注：
     *    焦点块为当前 mc_svg_tb对象框选数量为1时的块
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2019.4.18
     *      内容 : 所有代码
     ************************************************************************************************/
    this.set_focus = function () {
        var arr_sel = arr_box_select_obj;
        var arr_len = arr_sel.length;

        if (1 !== arr_len) {
            return;
        }

        var obj_cur = arr_sel[0];
        var dom_focus = document.activeElement;

        if (dom_focus === obj_cur) {
            return;
        }

        obj_cur.focus();
        set_cursor_position_to_end(obj_cur);
    };


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    走点数据组添加多组的css样式规则；
     * 参数:
     *    NA
     * 返回：
     *    NA
     * 例子：
     *    NA
     * 备注：
     *    NA
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2019.6.1
     *      内容 : 所有代码
     ************************************************************************************************/
    function add_grp_css_rules() {
        // 设置数据组颜色

        if (1 !== obj_external_mod_data.ui_grp_num) {
            var str_styles_idx = get_cur_stylesheets_idx();

            for (var color_i = 1; color_i <= obj_external_mod_data.ui_grp_num; color_i++) {
                var color_val = random_color();
                var str_class_name = "click_create_div_grp_" + color_i;
                var str_style_rule = "background:" + color_val + ";";

                arr_grp_color[color_i] = str_class_name;

                if ("undefined" !== str_styles_idx && false !== str_styles_idx) {
                    document.styleSheets[str_styles_idx].addRule("." + str_class_name, str_style_rule);
                }
            }
        }
    }

    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    随机生成css可用颜色值
     * 参数:
     *    NA
     * 返回：
     *    @returns { Promise<String> } 16进制颜色值
     * 例子：
     *    NA
     * 备注：
     *    颜色值设置了饱和度高的值
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2019.6.1
     *      内容 : 所有代码
     ************************************************************************************************/
    function random_color() {
        //  颜色值设置了饱和度高的值
        var arr_color = ["a", "b", "8", "9", "e", "f", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"];
        var color = "";

        for (var i = 0; 6 > i; i++) {
            var random = Math.floor(Math.random() * 16);

            color += arr_color[random];
        }

        return "#" + color;
    }


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    移除数据组样式
     * 参数:
     *    @param { Promise<Object> } obj 需要移除的成点对象
     * 返回：
     *    NA
     * 例子：
     *    NA
     * 备注：
     *    NA
     * 修改:
     *   1. 类型 : 创建
     *      作者 : 巫昭雯
     *      时间 : 2019.6.1
     *      内容 : 所有代码
     ************************************************************************************************/
    function remove_obj_grp_css_class(obj) {
        // var arr_grp_color = new Array(obj_external_mod_data.ui_grp_num);
        if (obj) {
            // var classList = obj.classList;
            var len = arr_grp_color.length;

            for (var i = 0; i < len; i++) {
                var item = arr_grp_color[i];

                obj.classList.remove(item);
            }
        }
    }


    // **************************************** mc_svg_tb 接口 **************************************** //
    // 获取拖拽块id
    this.get_drag_block_id = function () {
        return str_drag_virtual_div_id;
    };

    // 获取框选块id
    this.get_box_sel_id = function () {
        return box_selection_wrap_id;
    };

    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    鼠标在外部释放;
     * 参数:
     *    @param { Promise<String> } str_type 鼠标释放类型;  "drag" === 拖拽之后释放 || "box_sel" === 框选释放
     * 返回:
     *    NA
     * 例子:
     *    NA
     * 备注:
     *    1. "drag" : 后续操作为执行复制块操作；
     *    2. "box_sel": 执行框选操作
     * 修改:
     *    1. 类型 : 创建
     *       作者 : 巫昭雯
     *       时间 : 2020-12-12
     *       内容 : 所有代码
    ************************************************************************************************/
    this.onmouseup_at_svg_wrap = function (str_type) {
        if (!str_type) {
            return;
        }

        merge_call_operation("tb_io_clear_scroll_timer");

        switch (str_type) {
        case "drag":
            svg_item_content.onmouseup(window.event);
            break;
        case "box_sel":
            set_style_after_drag("外部释放", ui_selected_xy);
            break;
        default:
            break;
        }
    };

    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    从传参(填充数据)获取第一组数据（也就是应用数据）
     * 参数:
     *    @param { Promise<String> } arr 获取填充数据得到的数据
     * 返回:
     *    @returns { Promise<Array> } res 返回参数的 数据组数===1 的数据
     * 例子:
     *    NA
     * 备注:
     *    NA
     * 修改:
     *    1. 类型 : 创建
     *       作者 : 巫昭雯
     *       时间 : 2020-07-22
     *       内容 : 所有代码
    ************************************************************************************************/
    this.get_first_grp_data = function (arr) {
        if ("[object Array]" !== Object.prototype.toString.call(arr)) {
            return false;
        }

        var res = [];
        var len = arr.length;

        for (var idx = 0; idx < len; idx++) {
            var item = arr[idx];
            var grp_idx = item.grp_idx;

            if (1 === grp_idx) {
                var param = {
                    idx: item.idx,
                    sweep: item.sweep,
                    x: item.x,
                    y: item.y,
                    invalid_cnt: item.invalid_cnt,
                    grp_idx: grp_idx
                };

                res.push(param);
            }
        }

        return res;
    };


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    获取div_type值;
     * 参数:
     *    @param { Promise<Number> } key 定义的 obj_div_type 对象key
     * 返回:
     *    @returns { Promise<String> } 返回相应的字符串值 || false
     * 例子:
     *    NA
     * 备注:
     *    NA
     * 修改:
     *    1. 类型 : 创建
     *       作者 : 巫昭雯
     *       时间 : 2020-07-29
     *       内容 : 所有代码
    ************************************************************************************************/
    function get_div_type_val(key) {
        if ("number" !== typeof key) {
            return false;
        }

        // 定义div类型
        var obj_div_type = {
            // 生成块
            0: "create_block",
            // 编辑框
            1: "edit_box",
            // 背景小格子
            2: "svg_rect",
            // 虚点块
            3: "create_block_virtual_point"
        };

        return obj_div_type[key];
    }

    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    判断是否存在空点
     * 参数:
     *    NA
     * 返回:
     *    @returns { Promise<Boolean> } true === 存在 || false === 不存在
     * 例子:
     *    NA
     * 备注:
     *    NA
     * 修改:
     *    1. 类型 : 创建
     *       作者 : 巫昭雯
     *       时间 : 2020-09-28
     *       内容 : 所有代码
    ************************************************************************************************/
    this.has_empty_grid = function () {
        var arr_len = arr_create_obj.length;

        if (arr_len === ui_grid_num) {
            return false;
        }

        return true;
    };


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    从堆栈获取点数据
     * 参数:
     *    @param { Promise<String> } ui_cnt 该对象内的堆栈下标
     *    @param { Promise<Function> } callback 回调函数；
     * 返回:
     *    NA
     * 例子:
     *    NA
     * 备注:
     *    回调参数: 应用的点数据格式
     * 修改:
     *    1. 类型 : 创建
     *       作者 : 巫昭雯
     *       时间 : 2020-10-14
     *       内容 : 所有代码
    ************************************************************************************************/
    function get_point_data_from_stack (ui_cnt, callback) { // eslint-disable-line
        var arr_out = arr_block_data_change;
        var ui_out_last = arr_block_data_change.length - 1;

        var arr_in = arr_out[ui_out_last - ui_cnt];
        var arr_in_len = arr_in.length;
        var ui_start_in = arr_in_len - 1;

        for (var ui_in_i = ui_start_in; 0 <= ui_in_i; ui_in_i--) {
            var item_in = arr_in[ui_in_i];

            if (!item_in.b_is_init) {
                continue;
            }

            if ("function" === typeof callback) {
                var data = get_apply_data_format(item_in.div_id);

                callback(data);
            }
        }
    }

    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    获取生成块的应用数据格式
     * 参数:
     *    @param { Promise<String> } str_id 生成块id
     * 返回:
     *    @returns { Promise<Object> } 返回应用的数据格式对象 || false
     * 例子:
     *    NA
     * 备注:
     *    NA
     * 修改:
     *    1. 类型 : 创建
     *       作者 : 巫昭雯
     *       时间 : 2020-10-14
     *       内容 : 所有代码
    ************************************************************************************************/
    function get_apply_data_format(str_id) {
        if ("string" !== typeof str_id) {
            return false;
        }

        var obj = document.getElementById(str_id);

        if (obj) {
            var x_val = Number(obj.getAttribute("x"));
            var y_val = Number(obj.getAttribute("y"));
            var point = Number(obj.getAttribute("point"));
            var sweep = Number(obj.getAttribute("sweep"));
            var grp_idx = Number(obj.getAttribute("group_idx"));
            var val = obj.innerText;
            // var arr_int = val.match(cui_reg_int);
            var real_val = val;

            if (0 !== point) {
                var str_point = "[" + point + "]";

                real_val = val.replace(str_point, "");
            }

            var val_split = real_val;
            var idx_val = "";

            if (-1 !== real_val.indexOf("_")) {
                val_split = real_val.split("_");
                sweep = Number(val_split[0]);
                idx_val = val_split[1];
            }

            var obj_item_data = {
                idx: idx_val,
                sweep: sweep,
                x: x_val,
                y: y_val,
                invalid_cnt: point,
                grp_idx: grp_idx
            };

            return obj_item_data;
        }

        return false;
    }


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    获取点数 根据公式值排序
     * 参数:
     *    @param { Promise<String> } ui_cnt
     *    @param { Promise<String> } callback 回调函数；
     * 返回:
     *    NA
     * 例子:
     *    NA
     * 备注:
     *    回调参数
     *    @params ui_idx 数据保存下标
     *    @params obj_item_data 保存的数据格式
     * 修改:
     *    1. 类型 : 创建
     *       作者 : 巫昭雯
     *       时间 : 2020-10-23
     *       内容 : 所有代码
    ************************************************************************************************/
    function get_point_data_sort_by_formula(ui_cnt, callback) {
        var arr_create_div = arr_create_obj;
        var arr_len = arr_create_div.length;

        for (var idx = 0; idx < arr_len; idx++) {
            var item = arr_create_div[idx];
            var x_val = Number(item.getAttribute("x"));
            var y_val = Number(item.getAttribute("y"));
            var point = Number(item.getAttribute("point"));
            var pos = item.getAttribute("pos");
            var formula = item.getAttribute("formula");
            var ui_idx = null;

            try {
                ui_idx = Number(find_child_formula(pos, formula));
                if (false === ui_idx || 0 >= ui_idx) {
                    throw Error("改值错误");
                }
            } catch (e) {
                continue;
            }


            var obj_item_data = {
                x: x_val,
                y: y_val,
                invalid_cnt: point,
                color: m_str_color.toLocaleUpperCase()
            };


            if ("function" === typeof callback) {
                callback(ui_idx, obj_item_data);
            }
        }
    }


    // **************************************** 切线相关: 获取堆栈数据生成点, 切线模拟全局堆栈 **************************************** //
    // 模拟堆栈生成 点
    function render_stack_data(arr_stack, obj_map) {
        if (!mc_util_is_array(arr_stack) || !mc_util_is_object(obj_map)) {
            return false;
        }

        // get tb color
        var str_color = m_str_color;
        var obj_pos_c_map = new construct_color_pos_map();
        var ui_len = arr_stack.length;

        if (1 !== ui_len) {
            arr_block_data_change_sub = [];
            arr_block_data_change_sub.length = 0;
        }

        for (var ui_i = 0; ui_i < ui_len; ui_i++) {
            var obj_item = arr_stack[ui_i];

            if (obj_item && "[object Object]" === Object.prototype.toString.call(obj_item)) {
                var obj_new = mc_util_copy_obj(obj_item);
                var arr_int = obj_new.pos.match(cui_reg_int);
                var str_tb_key = obj_pos_c_map.get_key(str_color, Number(arr_int[0]), Number(arr_int[1]));

                if ("undefined" === typeof obj_map[str_tb_key] || "number" !== typeof obj_map[str_tb_key]) {
                    continue;
                }

                var str_id = obj_new.div_id;

                if (Number(obj_new.formula) > m_ui_max_fm_val) {
                    m_ui_max_fm_val = Number(obj_new.formula);
                }

                if (obj_new.b_is_init) {
                    create_div_from_rander_data(obj_new);

                    arr_box_select_obj = save_box_select_obj(obj_new.cur_arr_box_select);
                    obj_new.id = str_id;

                    if (1 === ui_len) {
                        // 入栈
                        set_val_change_action(obj_new, multiple_data_val_chg);
                        set_style_after_drag("渲染堆栈数据生成点", get_render_stack_box_select_dir(arr_box_select_obj));
                        continue;
                    }
                    set_val_change_action(obj_new);
                    set_style_after_drag("渲染堆栈数据生成点", get_render_stack_box_select_dir(arr_box_select_obj));
                    continue;
                }

                var obj_existed = document.getElementById(str_id);

                if (obj_existed) {
                    obj_existed.setAttribute("formula", obj_new.formula);
                    obj_existed.setAttribute("init_fml", obj_new.init_fml);
                    var obj_res = get_calc_sweep_formula_val(obj_new.formula);

                    obj_existed.innerText = obj_res.val;
                    obj_existed.pre_edit = obj_res.val;
                    // update formula
                    obj_pos_formula[obj_item.pos] = obj_new.formula;
                    // save box select
                    arr_box_select_obj = save_box_select_obj(obj_new.cur_arr_box_select);
                    obj_new.id = str_id;

                    if (1 === ui_len) {
                        set_val_change_action(obj_new, multiple_data_val_chg);
                        set_style_after_drag("渲染堆栈数据生成点", get_render_stack_box_select_dir(arr_box_select_obj));
                        continue;
                    }
                    set_val_change_action(obj_new);
                    set_style_after_drag("渲染堆栈数据生成点", get_render_stack_box_select_dir(arr_box_select_obj));
                }
            }
        }

        if (1 !== ui_len) {
            multiple_data_val_chg();
        }

        return true;
    }


    // 生成点从渲染数据
    function create_div_from_rander_data(obj) {
        var real_id = obj.div_id;
        var is_edit = false;
        var b_choosr = true;

        var arr_int = obj.pos.match(cui_reg_int);
        var x_val = arr_int[0];
        var y_val = arr_int[1];

        var top = (y_val - 1) * each_rect_height;
        var left = (x_val - 1) * each_rect_width;

        var formula = obj.formula;
        var init_fml = obj.init_fml;
        var pos_val = obj.pos;
        var sweep = obj.sweep;
        var point = Number(obj.point);

        var str_div_type = get_div_type_val(0);
        var str_class_name = "click_create_div";

        var obj_res = get_calc_sweep_formula_val(formula);
        var val = obj_res.val;

        if (0 !== point) {
            val = "[" + point + "]" + val;
        }

        // 第几组数据
        var grp_idx = Math.ceil(formula / obj_external_mod_data.ui_single_grp_num);

        var str_click_div = "<div id='" + real_id + "' div_type='" + str_div_type + "' is_choose='" + b_choosr + "' tabindex='0' contenteditable='" + is_edit + "' class='" + str_class_name + "' style='top:" + top + "px;left:" + left + "px;width:" + each_rect_width + "px;height:" + each_rect_height +
            "px;line-height:" + each_rect_height + "px;' x='" + x_val + "' y='" + y_val + "' pos='" + pos_val + "' init_fml = '" + init_fml + "'  formula='" + formula +
            "'sweep='" + sweep + "' point='" + point + "' group_idx='" + grp_idx + "'>" + val + "</div>";

        $obj_svg_jq.append(str_click_div);

        var obj_div_click = document.getElementById(real_id);

        add_eventListener_multiple_fun(obj_div_click, arr_point_event);
        obj_div_click.on_val_chg = create_div_on_val_change;

        // 设置生成之后就可以获得焦点；可触发键盘事件
        obj_div_click.focus();

        // 保存生成对象
        arr_create_obj.push(obj_div_click);
        // 更新位置对象
        obj_pos_formula[pos_val] = formula;

        set_edit_box_val(formula);
        set_hover_div_val(0);
        // update_formula_map_val();
        obj_div_click.pre_edit = val;
    }


    // 保存堆栈框选对象
    function save_box_select_obj(arr_obj) {
        var arr_res = [];

        if (!mc_util_is_array(arr_obj)) {
            return arr_res;
        }
        var arr_save_box_sel = arr_obj;
        var ui_box_sel_len = arr_save_box_sel.length;

        for (var ui_box_i = 0; ui_box_i < ui_box_sel_len; ui_box_i++) {
            var item_obj = arr_save_box_sel[ui_box_i];

            if (item_obj) {
                var obj_new_box_sel_i = document.getElementById(item_obj.id);

                if (obj_new_box_sel_i) {
                    arr_res.push(obj_new_box_sel_i);
                }
            }
        }

        return arr_res;
    }


    // 获取渲染堆栈数据框选对象方向
    function get_render_stack_box_select_dir(arr_box_sel) {
        var ui_len = arr_box_sel.length;
        var ui_dir = null;

        remove_selected_div_class();
        set_box_selecte_style(arr_box_sel);

        // 判断方向
        if (2 <= ui_len) {
            var obj_one = arr_box_sel[0];
            var obj_two = arr_box_sel[1];

            if (obj_one.offsetTop === obj_two.offsetTop) {
                ui_dir = 0;
            } else if (obj_one.offsetLeft === obj_two.offsetLeft) {
                ui_dir = 1;
            }
        }

        return ui_dir;
    }
    // **************************************** 切线相关: 获取堆栈数据生成点, 切线模拟全局堆栈 **************************************** //


    // **************************************** 背景高亮相关 **************************************** //
    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    初始化创建背景高亮; 创建 rect 和 defs
     * 参数:
     *    @param { Promise<String> } id 高亮条填充的 pattern id
     * 返回:
     *    NA
     * 例子:
     *    NA
     * 备注:
     *    每隔八个格子高亮; 插入到背景svg的第一个小孩之前
     * 修改:
     *    1. 类型 : 创建
     *       作者 : 巫昭雯
     *       时间 : 2020-12-12
     *       内容 : 所有代码
    ************************************************************************************************/
    function init_bgc_highlight(id) {
        if (id) {
            var obj_svg_content = document.getElementById(svg_item_content_id);

            if (obj_svg_content) {
                var obj_bgc_x = new create_bgc_highlight_rect(obj_svg_content);
                var obj_bgc_y = new create_bgc_highlight_rect(obj_svg_content);

                obj_bgc_x.set_pattern_id(id);
                obj_bgc_x.set_direction("X");
                obj_bgc_x.set_number(Math.floor(row / 8));

                obj_bgc_y.set_pattern_id(id);
                obj_bgc_y.set_direction("Y");
                obj_bgc_y.set_number(Math.floor(col / 8));

                obj_bgc_x.create_done();
                obj_bgc_y.create_done();

                var obj_fisrt_chi = obj_svg_content.firstChild;
                var obj_defs_dom = obj_bgc_x.get_defs();

                if (obj_fisrt_chi) {
                    // defs
                    obj_svg_content.insertBefore(obj_defs_dom, obj_fisrt_chi);
                }
            }
        }
    }


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    设置背景高亮缩放
     * 参数:
     *    @param { Promise<Object> } obj  高亮背景 rect对象数组(分方向)
     *    @param { Promise<String> } str_dir 方向; "X" === X轴 || "Y" === Y轴
     * 返回:
     *    NA
     * 例子:
     *    NA
     * 备注:
     *    X轴处理高属性; Y轴处理宽属性
     * 修改:
     *    1. 类型 : 创建
     *       作者 : 巫昭雯
     *       时间 : 2020-12-12
     *       内容 : 所有代码
    ************************************************************************************************/
    function set_bgc_hl_zoom(obj, str_dir) {
        if (!str_dir || !obj) {
            return;
        }

        var ui_len = obj.length;

        if (0 === ui_len) {
            return;
        }

        var str_attr = "";
        var str_attr_xy = "";
        var ui_attr_val = 0;
        var ui_step = 0;
        var ui_rect_size = 0;

        if ("X" === str_dir) {
            str_attr = "height";
            str_attr_xy = "y";
            ui_attr_val = each_rect_height;
            ui_rect_size = each_rect_height;
            ui_step = 8 * each_rect_height;
        } else {
            str_attr = "width";
            str_attr_xy = "x";
            ui_attr_val = each_rect_width;
            ui_rect_size = each_rect_width;
            ui_step = 8 * each_rect_width;
        }

        for (var ui_i = 0; ui_i < ui_len; ui_i++) {
            var obj_el = obj[ui_i];

            if (!obj_el) {
                continue;
            }
            obj_el.setAttribute(str_attr, ui_attr_val);
            obj_el.setAttribute(str_attr_xy, (ui_step * (ui_i + 1)) - ui_rect_size);
        }
    }
    // **************************************** 新添加函数 **************************************** //


    // **************************************** mc_svg_tb 接口 **************************************** //
    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    渲染之前亲清空堆栈
     * 参数:
     *    NA
     * 返回:
     *    NA
     * 例子:
     *    NA
     * 备注:
     *    NA
     * 修改:
     *    1. 类型 : 创建
     *       作者 : 巫昭雯
     *       时间 : 2020-10-14
     *       内容 : 所有代码
    ************************************************************************************************/
    this.tb_io_reset_render_stack = function () {
        arr_block_data_change_sub = [];
    };

    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    渲染完成之后设置数据堆栈
     * 参数:
     *    NA
     * 返回:
     *    NA
     * 例子:
     *    NA
     * 备注:
     *    NA
     * 修改:
     *    1. 类型 : 创建
     *       作者 : 巫昭雯
     *       时间 : 2020-10-14
     *       内容 : 所有代码
    ************************************************************************************************/
    this.tb_io_set_render_stack = function () {
        multiple_data_val_chg();
        return;
    };

    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    获取生成块
     * 参数:
     *    NA
     * 返回:
     *    @returns { Promise<Array> } 返回拷贝的生成块集合
     * 例子:
     *    NA
     * 备注:
     *    NA
     * 修改:
     *    1. 类型 : 创建
     *       作者 : 巫昭雯
     *       时间 : 2020-10-14
     *       内容 : 所有代码
    ************************************************************************************************/
    this.tb_io_get_create_block = function () {
        var copy_arr_obj = arr_create_obj.slice(0);

        return copy_arr_obj;
    };

    /************************************************************************************************
     * 类型:
     *    函数;对外接口
     * 功能:
     *    获取点数据
     * 参数:
     *    @param { Promise<String> } ui_cnt 堆栈下标
     *    @param { Promise<Function> } callback 回调函数；
     * 返回:
     *    NA
     * 例子:
     *    NA
     * 备注:
     *    回调参数: 应用的点数据格式
     * 修改:
     *    1. 类型 : 创建
     *       作者 : 巫昭雯
     *       时间 : 2020-10-14
     *       内容 : 所有代码
    ************************************************************************************************/
    this.tb_io_get_point_data = function (ui_cnt, callback) {
        // get_point_data_from_stack(ui_cnt, callback);
        get_point_data_sort_by_formula(ui_cnt, callback);
    };

    // 获取最大值(最大公式值)
    this.tb_io_get_max_val = function () {
        return m_ui_max_fm_val;
    };

    // 获取该表格颜色
    this.tb_io_get_color = function () {
        return m_str_color;
    };

    // 设置该表格颜色
    this.tb_io_set_color = function (str) {
        if ("string" !== typeof str) {
            return false;
        }
        m_str_color = str;
        return true;
    };

    // 获取表格内部堆栈
    this.tb_io_get_stack = function () {
        return arr_block_data_change;
    };

    // 渲染堆栈数据
    this.tb_io_render_stack_data = function (arr, obj_map) {
        return render_stack_data(arr, obj_map);
    };

    // 清除滚动条定时器
    this.tb_io_clear_scroll_timer = function () {
        if (null !== m_ui_timer) {
            clearInterval(m_ui_timer);
            m_ui_timer = null;
        }
    };

    return true;
}


// **************************************** 全局函数 **************************************** //
/************************************************************************************************
* 类型:
*    函数
* 功能:
*    多个mc_svg_tb对象数据变动回调
* 参数:
*    @param { Promise<Object>} obj 需要堆栈的数据对象
* 返回：
*    NA
* 例子：
*    NA
* 备注：
*    数组 arr_action_stack 为外层创建数据变动数组
* 修改:
*   1. 类型 : 创建
*      作者 : 巫昭雯
*      时间 : 2020.4.18
*      内容 : 所有代码
************************************************************************************************/
function mc_svg_on_val_chg_callback(obj) {
    // 不为空对象
    if ("{}" === JSON.stringify(obj)) {
        return;
    }

    //虚点堆栈
    if (obj.b_vpoint_stack) {
        var arr_action_stack_len = arr_action_stack.length;

        // 当堆栈为空时直接入栈
        if (0 === arr_action_stack_len) {
            arr_action_stack.push(obj);
            return;
        }

        // 堆栈不为空时 如找到堆栈的svg_tab对象则停止 || 入栈
        for (var idx = 0; idx < arr_action_stack_len; idx++) {
            var stack_item = arr_action_stack[idx];

            if (stack_item.table_idx === obj.table_idx) {
                return;
            }
        }

        arr_action_stack.push(obj);
    } else {
        // 数据堆栈
        arr_action_stack.push(obj);
    }
}


/************************************************************************************************
* 类型:
*    函数
* 功能:
*    获取当前触发的mc_svg_tb对象在全局mc_svg_tb对象集合的下标
* 参数:
*    @param { Promise<String>} id  存放当前mc_svg_tb对象的容器id
* 返回：
*    @returns { Promise<String>} 相应下标 || false
* 例子：
*    NA
* 备注：
*    NA
* 修改:
*   1. 类型 : 创建
*      作者 : 巫昭雯
*      时间 : 2020.4.18
*      内容 : 所有代码
************************************************************************************************/
function get_g_obj_idx(id) {
    for (var idx = 0; idx < arr_svg_obj.length; idx++) {
        var item = arr_svg_obj[idx];
        var item_id = item.get_container_id();

        if (item_id === id) {
            return idx;
        }
    }
    return false;
}


// 对象选中操作
function mc_svg_gp_select_cur(id) {
    var cur_id = id;

    return set_select_cur_svg();

    function set_select_cur_svg() {
        for (var gp_idx = 0; gp_idx < g_arr_svg_gp.length; gp_idx++) {
            var gp_item = g_arr_svg_gp[gp_idx];

            if (gp_item instanceof mc_svg_tb) {
                gp_item.b_is_cur_obj = false;
                if (gp_item.get_container_id() === cur_id) {
                    gp_item.b_is_cur_obj = true;
                }
            }
        }
        return false;
    }
}


/************************************************************************************************
* 类型:
*    函数
* 功能:
*    多个mc_svg_tb对象调用内置的属性方法合并操作
* 参数:
*    @param { Promise<String>} fun_name 调用对象的方法名
*    @param { Promise<Array>} arr_param 可选参数；若存在必须为数组；调用方法的传参
* 返回：
*    @returns { Promise<Array> } res 返回单个调用方法得到的数据组成的数组
* 例子：
*    NA
* 备注：
*    数组 arr_svg_obj 为创建 svg_table 对象的集合
* 修改:
*   1. 类型 : 创建
*      作者 : 巫昭雯
*      时间 : 2020.4.3
*      内容 : 所有代码
************************************************************************************************/
function merge_call_operation(fun_name, arr_param) {
    var res = [];

    for (var idx = 0; idx < arr_svg_obj.length; idx++) {
        var item = arr_svg_obj[idx];

        if ("function" === typeof item[fun_name] && Object.prototype.hasOwnProperty.call(item, fun_name)) {
            if ("undefined" === typeof arr_param) {
                var result_no_param = item[fun_name](arr_param);

                res.push(result_no_param);
            } else {
                if ("[object Array]" !== Object.prototype.toString.call(arr_param)) {
                    res[idx] = false;
                    continue;
                }

                var result_has_param = item[fun_name](arr_param[idx]);

                res.push(result_has_param);
            }
        }
    }

    return res;
}


/************************************************************************************************
 * 类型:
 *    函数
 * 功能:
 *    multiple table obj merge operation create array params; 配合 merge_call_operation 函数使用
 * 参数:
 *    @param { Promise<All> } param 相同的参数
 * 返回:
 *    @returns { Promise<Array> } 参数组合数组;
 * 例子:
 *    NA
 * 备注:
 *    NA
 * 修改:
 *    1. 类型 : 创建
 *       作者 : 巫昭雯
 *       时间 : 2020-10-14
 *       内容 : 所有代码
************************************************************************************************/
function get_merge_param(param) {
    var ui_len = arr_svg_obj.length;
    var arr_param = [];

    for (var ui_i = 0; ui_i < ui_len; ui_i++) {
        arr_param.push(param);
    }

    return arr_param;
}


/************************************************************************************************
* 类型:
*    函数
* 功能:
*    滚动条回调函数
* 参数:
*    @param { Promise<Object>} event 滚动条事件对象event
* 返回：
*    NA
* 例子：
*    NA
* 备注：
*    数组 arr_svg_obj 为创建 svg_table 对象的集合
* 修改:
*   1. 类型 : 创建
*      作者 : 巫昭雯
*      时间 : 2020.4.3
*      内容 : 所有代码
************************************************************************************************/
function scroll_callback(event) {
    var scroll_left = event.scrollLeft;
    var scroll_top = event.scrollTop;

    for (var idx = 0; idx < arr_svg_obj.length; idx++) {
        var item = arr_svg_obj[idx];
        var item_id = item.get_scroll_id();

        if (item_id !== event.id && item.scrollLeft !== scroll_left && item.scrollTop !== scroll_top ) {
            item.link_scroll(item_id, scroll_left, scroll_top);
            item.set_link_scroll_fn();
        }
    }
}


/************************************************************************************************
 * 类型:
 *    函数
 * 功能:
 *    获取全局点击数 === 全局的生成点个数总和
 * 参数:
 *    NA
 * 返回:
 *    @returns { Promise<Number> } 生成点个数
 * 例子:
 *    NA
 * 备注:
 *    NA
 * 修改:
 *    1. 类型 : 创建
 *       作者 : 巫昭雯
 *       时间 : 2020-10-19
 *       内容 : 所有代码
************************************************************************************************/
function get_g_click_cnt() {  // eslint-disable-line
    var ui_cnt = 0;
    var ui_len = arr_svg_obj.length;

    for (var idx = 0; idx < ui_len; idx++) {
        var obj_tb = arr_svg_obj[idx];

        ui_cnt += obj_tb.get_create_num();
    }

    return ui_cnt;
}


/************************************************************************************************
* 类型:
*    函数
* 功能:
*    获取单项虚点数据
* 参数:
*    @param { Promise<Number> } len 单项虚点总长度 === 虚点个数
*    @param { Promise<String> } idx 虚点idx
*    @param { Promise<Number> } sweep 虚点行扫数
* 返回：
*    @returns { Promise<Array> } 返回虚点数数组
* 例子：
*    NA
* 备注：
*    NA
* 修改:
*   1. 类型 : 创建
*      作者 : 巫昭雯
*      时间 : 2020.4.2
*      内容 : 所有代码
************************************************************************************************/
function get_virtual_point_data(len, idx, sweep) {
    var virtual_point_data = [];

    for (var j = 0; j < len; j++) {
        virtual_point_data.push({
            idx: idx,
            sweep: sweep,
            x: 0,
            y: 0
        });
    }

    return virtual_point_data;
}


/************************************************************************************************
 * 类型:
 *    函数
 * 功能:
 *    获取全局最大值(公式值)
 * 参数:
 *    NA
 * 返回:
 *    @returns { Promise<Number> }  最大的公式值
 * 例子:
 *    NA
 * 备注:
 *    NA
 * 修改:
 *    1. 类型 : 创建
 *       作者 : 巫昭雯
 *       时间 : 2020-10-20
 *       内容 : 所有代码
************************************************************************************************/
function get_g_max_val() {
    var ui_max = 0;
    var ui_len_tb = arr_svg_obj.length;

    for (var ui_i = 0; ui_i < ui_len_tb; ui_i++) {
        var item = arr_svg_obj[ui_i];
        var ui_max_i = item.tb_io_get_max_val();

        if (ui_max_i > ui_max) {
            ui_max = ui_max_i;
        }
    }

    return ui_max;
}


/************************************************************************************************
 * 类型:
 *    函数
 * 功能:
 *    更新全局表格的虚点值；
 * 参数:
 *    @param { Promise<Number> }  ui_val 虚点值
 * 返回:
 *    @returns { Promise<Boolean> } 设置成功 || 失败
 * 例子:
 *    NA
 * 备注:
 *    NA
 * 修改:
 *    1. 类型 : 创建
 *       作者 : 巫昭雯
 *       时间 : 2020-10-23
 *       内容 : 所有代码
************************************************************************************************/
function update_g_vpoint_val(ui_val) {
    if ("number" !== typeof ui_val) {
        return false;
    }

    merge_call_operation("set_vpoint_val", get_merge_param(ui_val));
    return true;
}


// **************************************** 数据格式 **************************************** //
/************************************************************************************************
 * 类型:
 *    函数
 * 功能:
 *    设置全局的页面数据处理函数
 * 参数:
 *    NA
 * 返回:
 *    NA
 * 例子:
 *    NA
 * 备注:
 *    NA
 * 修改:
 *    1. 类型 : 创建
 *       作者 : 巫昭雯
 *       时间 : 2020-10-23
 *       内容 : 所有代码
************************************************************************************************/
function mc_util_tb_data() {
    var m_arr_page_data = [];
    var m_obj_map = {};
    var m_ui_line_len = 0;

    // 构建map 属性值格式
    var m_obj_stack = function (g_stack, tb_stack) {
        return {
            g: g_stack,
            m: tb_stack
        };
    };


    this.get_data = function () {
        return m_arr_page_data;
    };


    this.reset_data = function () {
        m_arr_page_data = [];
        m_arr_page_data.length = 0;
    };

    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    设置最大数据线
     * 参数:
     *    @param { Promise<Number> } ui_line 数据线最大值
     * 返回:
     *    @returns { Promise<Boolean> } true || false 设置成功
     * 例子:
     *    NA
     * 备注:
     *    NA
     * 修改:
     *    1. 类型 : 创建
     *       作者 : 巫昭雯
     *       时间 : 2020-10-23
     *       内容 : 所有代码
    ************************************************************************************************/
    function set_max_line(ui_line) {
        if ("number" !== typeof ui_line) {
            return false;
        }
        m_ui_line_len = ui_line;
        return true;
    }

    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    设置堆栈
     * 参数:
     *    @param { Promise<Array> } g_stack 全局堆栈
     *    @param { Promise<Array> } tb_stack 表格内堆栈
     *    @param { Promise<Number> } ui_date_line 设置的数据线堆栈
     * 返回:
     *    @returns { Promise<Boolean> } 设置成功与否
     * 例子:
     *    NA
     * 备注:
     *    NA
     * 修改:
     *    1. 类型 : 创建
     *       作者 : 巫昭雯
     *       时间 : 2020-10-23
     *       内容 : 所有代码
    ************************************************************************************************/
    function set_stack(g_stack, tb_stack, ui_date_line) {
        if (!mc_util_is_array(g_stack) || !mc_util_is_array(tb_stack) || "number" !== typeof ui_date_line) {
            return false;
        }

        var obj_stack = new m_obj_stack(g_stack, tb_stack);

        // -1 === 全部数据线
        if (-1 === ui_date_line) {
            // is all date line
            m_obj_map[ui_date_line] = obj_stack;

            for (var ui_i = 1; ui_i <= m_ui_line_len; ui_i++) {
                var ui_key = ui_i;
                var obj_stack_i = new m_obj_stack(g_stack, tb_stack);

                m_obj_map[ui_key] = obj_stack_i;
            }
            return true;
        }
        m_obj_map[ui_date_line] = obj_stack;

        return true;
    }


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    获取堆栈
     * 参数:
     *    @param { Promise<String> } ui_data_line 获取的是那一条数据线的堆栈
     *    @param { Promise<String> } key 全局  === "g" || 页面堆栈 === "m"
     * 返回:
     *    @returns { Promise<String> } 堆栈数据数组
     * 例子:
     *    NA
     * 备注:
     *    NA
     * 修改:
     *    1. 类型 : 创建
     *       作者 : 巫昭雯
     *       时间 : 2020-10-23
     *       内容 : 所有代码
    ************************************************************************************************/
    function get_stack(ui_data_line, key) {
        if ("number" !== typeof ui_data_line) {
            return {};
        }

        var obj_stack = m_obj_map[ui_data_line];

        if ("{}" === JSON.stringify(obj_stack)) {
            return {};
        }

        return obj_stack[key];
    }


    // ******************************************* mc_util_tb_data io接口 ********************************************** //
    // **************************************** 数据线 **************************************** //
    this.tb_data_io_set_stack = function (g_stack, tb_stack, ui_date_line) {
        return set_stack(g_stack, tb_stack, ui_date_line);
    };

    this.tb_data_io_get_g_stack = function (ui_data_line) {
        return get_stack(ui_data_line, "g");
    };

    this.tb_data_io_get_tb_stack = function (ui_data_line) {
        return get_stack(ui_data_line, "m");
    };

    this.tb_data_io_set_max_line = function (ui_line) {
        return set_max_line(ui_line);
    };

    // **************************************** 其他 **************************************** //
}


/************************************************************************************************
 * 类型:
 *    函数; 构造函数
 * 功能:
 *    构造颜色值和位置值的map对象
 * 参数:
 *    NA
 * 返回:
 *    NA
 * 例子:
 *    NA
 * 备注:
 *    NA
 * 修改:
 *    1. 类型 : 创建
 *       作者 : 巫昭雯
 *       时间 : 2020-10-22
 *       内容 : 所有代码
************************************************************************************************/
function construct_color_pos_map() {
    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    函数功能
     * 参数:
     *    @param { Promise<String> } arr_data 需要转换数据对象数组。该数据对象格式为保存的页面数据;
     *    @param { Promise<String> } ui_tb_len 表格对象长度(个数)
     * 返回:
     *    @returns { Promise<String> } 参数1
     * 例子:
     *    NA
     * 备注:
     *    NA
     * 修改:
     *    1. 类型 : 创建
     *       作者 : 巫昭雯
     *       时间 : 2020-10-22
     *       内容 : 所有代码
    ************************************************************************************************/
    this.get_map_obj_data = function (arr_data, ui_tb_len) {
        if (!mc_util_is_array(arr_data) || "number" !== typeof ui_tb_len) {
            return false;
        }
        // color_x_y === primary key
        // this return obj === { color_x_y : invalid_cnt }
        var obj = {};
        var ui_len = arr_data.length;

        for (var ui_i = 0; ui_i < ui_len; ui_i++) {
            var item = arr_data[ui_i];

            if (item) {
                var str_key = "";

                if (1 === ui_tb_len) {
                    str_key = this.get_key("rgb", item.x, item.y);
                } else {
                    str_key = this.get_key(item.color, item.x, item.y);
                }

                obj[str_key] = item.invalid_cnt;
            }
        }
        return obj;
    };


    /************************************************************************************************
     * 类型:
     *    函数
     * 功能:
     *    获取key字符串
     * 参数:
     *    @param { Promise<String> } str_c 颜色值
     *    @param { Promise<Number> } ui_x 位置值 X
     *    @param { Promise<Number> } ui_y 位置值 Y
     * 返回:
     *    @returns { Promise<String> } 根据颜色值和位置值拼接的key值  || false
     * 例子:
     *    NA
     * 备注:
     *    NA
     * 修改:
     *    1. 类型 : 创建
     *       作者 : 巫昭雯
     *       时间 : 2020-10-22
     *       内容 : 所有代码
    ************************************************************************************************/
    this.get_key = function (str_c, ui_x, ui_y) {
        if ("string" !== typeof str_c || "number" !== typeof ui_x || "number" !== typeof ui_y) {
            return false;
        }

        var str_key = str_c.toLocaleLowerCase() + "_" + ui_x + "_" + ui_y;

        return str_key;
    };
}
